diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 00000000..46a4f0b9 --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,5 @@ +reviews: + auto_review: + enabled: true + auto_incremental_review: true + drafts: false diff --git a/.gemini/config.yaml b/.gemini/config.yaml new file mode 100644 index 00000000..3b5e413e --- /dev/null +++ b/.gemini/config.yaml @@ -0,0 +1,23 @@ +have_fun: false + +ignore_patterns: + - "**/charts/**" + - "**/vendor/**" + - "**/zz_generated.*.go" + - "**/pkg/generated/**" + - "**/_out/**" + - "**/*.tgz" + - "**/dashboards/**/*.json" + - "**/*.patch" + - "**/*.diff" + - "**/images/*.json" + +code_review: + disable: false + comment_severity_threshold: LOW + max_review_comments: 50 + pull_request_opened: + help: false + summary: true + code_review: true + include_drafts: false diff --git a/.gemini/styleguide.md b/.gemini/styleguide.md new file mode 100644 index 00000000..7185a625 --- /dev/null +++ b/.gemini/styleguide.md @@ -0,0 +1,110 @@ +# Cozystack Review Guidelines + +## Project Architecture + +Cozystack is a Kubernetes-based PaaS built on Helm umbrella charts and FluxCD. +Packages live in `packages/{core,system,apps,extra,library,tests}/`. The `library/` group holds reusable helper charts; the `tests/` group exists because library charts are not directly testable. +Each package wraps one or more upstream Helm charts. +Go code in `cmd/`, `internal/`, `pkg/` implements Kubernetes controllers and API server. +Static CRDs are generated by `hack/update-codegen.sh` from types under `api/v1alpha1/`, `api/backups/`, and `api/dashboard/`. +App Kinds (like `Postgres`, `Kafka`) live in `api/apps/v1alpha1/` but are registered dynamically at runtime from `ApplicationDefinition` resources — they are not static CRDs. + +## Vendored Code — Critical Rules + +**Never suggest editing files inside any `charts/` directory under `packages/`.** +Those are upstream Helm charts vendored via `make update` (which runs `helm pull`). +Any direct edit is overwritten on the next update and provides zero value. + +If you find an issue that appears to live in vendored chart code: + +- For configuration-level changes: suggest overrides in the package root `values.yaml`. +- For structural changes: suggest a patch file in `packages//patches/` applied by the Makefile. +- For source-code changes in images: suggest a patch in `packages//images//patches/`. +- For true upstream bugs: point to the upstream repository and suggest an upstream issue/PR. +- Do NOT suggest creating `charts/patches/` — patches never live inside `charts/`. + +Similarly, never propose edits to: + +- `vendor/` — Go dependencies. Changes go through `go get` and `go mod tidy`. +- `zz_generated.*.go` — regenerated by `make generate`. +- `pkg/generated/` — auto-generated Kubernetes client code. +- Image digest values in `values.yaml` — set by CI via `make image`, not by humans. +- `go.mod` / `go.sum` by hand — use `go get` and `go mod tidy`. + +## Commit and PR Requirements + +Each commit must follow [Conventional Commits](https://www.conventionalcommits.org/) format: `type(scope): brief description`. + +Valid types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`. + +Valid scopes: + +- System: `dashboard`, `platform`, `cilium`, `kube-ovn`, `linstor`, `fluxcd`, `cluster-api` +- Apps: `postgres`, `mariadb`, `redis`, `kafka`, `clickhouse`, `kubernetes`, `virtual-machine` +- Meta: `api`, `hack`, `tests`, `ci`, `docs` +- Package-specific: any `` matching a directory under `packages/` + +Breaking changes: append `!` after type/scope (`feat(api)!: ...`) or add a `BREAKING CHANGE:` footer. + +Each commit must have a `Signed-off-by:` trailer (produced by `git commit --signoff`). + +PR body must contain a release note block: + +````text +```release-note +type(scope): human-readable changelog entry +``` +```` + +Flag any PR whose commits lack the Conventional Commits format or signoff, or whose body has no release-note block. + +## Helm Chart Conventions + +Packages follow an umbrella chart pattern: + +- `charts/` — vendored upstream, read-only +- `templates/` — Cozystack-specific extra manifests +- `values.yaml` — override values for the upstream chart +- `values.schema.json` — JSON Schema for dashboard UI and input validation + +When reviewing `values.schema.json`: + +- `make generate` regenerates this file and strips `title`, `description`, and `x-*` custom annotations. Do not suggest adding fields that will be stripped on the next regeneration. +- Focus on type correctness, `required` fields, `enum` values, and default values. +- Flag breaking changes: removing a field, changing its type, or narrowing its enum. + +## Sensitive Components + +**`packages/core/platform/`**: the platform chart that deploys everything. Changes here can require updates to the migration flow — the Helm hook in `packages/core/platform/templates/migration-hook.yaml` and the runner image with scripts in `packages/core/platform/images/migrations/`. Flag any change to this package that lacks a corresponding migration update or an explicit note that backward compatibility is preserved. + +**`api/apps/v1alpha1/`**: app CRD Kinds are registered at runtime from `ApplicationDefinition` resources and the matching `values.schema.json`. Suggest changes to the relevant package schema rather than hand-editing generated types. + +**RBAC, ServiceAccounts, and SecurityContext**: flag overly broad RBAC (`*` on resources or verbs without justification), missing `securityContext`, containers running as root without an explicit reason, and `hostPath`/`hostNetwork` usage without clear rationale. + +## Go Code Standards + +- Use controller-runtime patterns for reconcilers. +- Use structured logging via `logr` — flag `fmt.Print*` and `log.Print*` in controller code. +- Handle errors explicitly. Discarding meaningful errors with `_` is a bug. +- Propagate `context.Context` through call chains. Flag `context.Background()` created inside a reconciler or request handler. +- Prefer `ctrl.Result{RequeueAfter: ...}` over empty requeue for predictable reconciliation loops. +- Tests live beside the code (`*_test.go`). New behavior without tests is worth flagging. + +## What to Review Carefully + +- Logic errors, off-by-one bugs, nil dereferences. +- Missing error handling, especially in reconcilers and API handlers. +- Helm template correctness: missing `quote`, incorrect indentation, wrong scope in `with`/`range`. +- Security: permissive RBAC, privileged containers, secrets in environment variables, hardcoded credentials. +- Missing resource requests/limits on new workloads. +- Breaking changes in `values.schema.json`: removed fields, tightened types, narrower enums. + +## Anti-patterns — Do Not Flag These + +- Large JSON files in `dashboards/` — imported from upstream Grafana sources, not hand-written. +- Files under any `charts/` directory — vendored upstream, left as-is intentionally. +- Whitespace or formatting in `*.patch` / `*.diff` files — machine-applied, not authored. +- Missing comments on generated code. +- `go.sum` changes accompanying `go.mod` changes — expected and correct. +- Fork relationships for vendored tooling images — intentional (e.g., `cozystack/kilo` fork is expected). +- Absence of unit tests for vendored chart overrides — covered by E2E tests in `hack/e2e-apps/`. diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 716776ea..2482712d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @kvaps @lllamnyp @lexfrei @androndo @IvanHunters +* @kvaps @lllamnyp @lexfrei @androndo @IvanHunters @sircthulhu @myasnikovdaniil diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 1a119259..fefdab82 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,7 +1,7 @@ --- name: Bug report about: Create a report to help us improve -labels: 'bug' +labels: 'kind/bug' assignees: '' --- diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 9a52457c..044dd6a0 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,8 +1,11 @@ + ### Release note ```release-note -[] + ``` \ No newline at end of file diff --git a/.github/labels.yml b/.github/labels.yml new file mode 100644 index 00000000..2ce04130 --- /dev/null +++ b/.github/labels.yml @@ -0,0 +1,371 @@ +# Cozystack repository labels +# +# Label conventions follow the Kubernetes scheme: +# https://github.com/kubernetes/test-infra/blob/master/label_sync/labels.md +# +# Synced into the repository by .github/workflows/labels.yaml +# (EndBug/label-sync@v2). Edit this file via pull request — UI changes +# will be overwritten on the next sync. +# +# Constraints (enforced by the validate job in labels.yaml): +# - description ≤ 100 characters (GitHub REST API limit) +# - color is a 6-character hex string (no leading #) +# - label names are unique +# - aliases do not collide with top-level names +# +# Categories: +# kind/ issue or PR type +# priority/ urgency +# triage/ review state +# lifecycle/ issue or PR lifecycle +# area/ subsystem; extensible — add when 3+ open issues exist +# do-not-merge/ PR merge blockers +# security/ security-finding severity and status (Cozystack-specific) +# size: PR size (auto-applied) +# +# `aliases:` lets EndBug/label-sync rename existing labels without losing +# references on already-tagged issues and PRs. +# +# GitHub-default labels not migrated here (`wontfix`, `invalid`) currently +# carry zero issues/PRs in this repo and will be removed in a follow-up +# cleanup PR rather than aliased to a different namespace. + +# ────────────────────────────────────────────── +# kind/ — issue or PR type +# ────────────────────────────────────────────── + +- name: kind/bug + color: 'd73a4a' + description: Categorizes issue or PR as related to a bug + aliases: ['bug'] + +- name: kind/feature + color: 'a2eeef' + description: Categorizes issue or PR as related to a new feature + aliases: ['enhancement'] + +- name: kind/documentation + color: '0075ca' + description: Categorizes issue or PR as related to documentation + aliases: ['documentation'] + +- name: kind/support + color: 'd876e3' + description: Categorizes issue as a support question + aliases: ['question'] + +- name: kind/cleanup + color: 'c7def8' + description: Categorizes issue or PR as related to cleanup of code, process, or technical debt + +- name: kind/regression + color: 'e11d21' + description: Categorizes issue or PR as related to a regression from a prior release + +- name: kind/flake + color: 'f7c6c7' + description: Categorizes issue or PR as related to a flaky test + +- name: kind/failing-test + color: 'e11d21' + description: Categorizes issue or PR as related to a consistently or frequently failing test + +- name: kind/api-change + color: 'c7def8' + description: Categorizes issue or PR as related to adding, removing, or otherwise changing an API + +- name: kind/breaking-change + color: 'e11d21' + description: Indicates the change introduces a breaking API or behaviour change + +# ────────────────────────────────────────────── +# priority/ — urgency +# ────────────────────────────────────────────── + +- name: priority/critical-urgent + color: 'e11d21' + description: Highest priority. Must be actively worked on as someone's top priority right now + +- name: priority/important-soon + color: 'eb6420' + description: Must be staffed and worked on either currently, or very soon, ideally in time for the next release + +- name: priority/important-longterm + color: 'fbca04' + description: Important over the long term, but may not be staffed and/or may need multiple releases to complete + +- name: priority/backlog + color: 'fef2c0' + description: General backlog priority. Lower than priority/important-longterm + +# ────────────────────────────────────────────── +# triage/ — review state +# ────────────────────────────────────────────── + +- name: triage/needs-triage + color: 'ededed' + description: Indicates an issue needs triage by a maintainer + +- name: triage/accepted + color: '0e8a16' + description: Indicates an issue is ready to be actively worked on + +- name: triage/needs-information + color: 'fbca04' + description: Indicates an issue needs more information in order to work on it + +- name: triage/not-reproducible + color: 'fbca04' + description: Indicates an issue can not be reproduced as described + +- name: triage/duplicate + color: 'cfd3d7' + description: Indicates an issue is a duplicate of another issue + aliases: ['duplicate'] + +- name: triage/unresolved + color: 'cfd3d7' + description: Indicates an issue that can not or will not be resolved + +# ────────────────────────────────────────────── +# lifecycle/ — issue or PR lifecycle +# ────────────────────────────────────────────── + +- name: lifecycle/active + color: '1d76db' + description: Indicates that an issue or PR is actively being worked on by a contributor + +- name: lifecycle/frozen + color: 'db5dd6' + description: Indicates that an issue or PR should not be auto-closed due to staleness + aliases: ['frozen'] + +- name: lifecycle/stale + color: 'dadada' + description: Denotes an issue or PR has remained open with no activity and has become stale + aliases: ['stale'] + +- name: lifecycle/rotten + color: '795548' + description: Denotes an issue or PR that has aged beyond stale and will be auto-closed + +# ────────────────────────────────────────────── +# area/ — subsystem (extensible) +# Add a new area/* when there are 3+ open issues on the topic. +# ────────────────────────────────────────────── + +- name: area/api + color: 'bfd4f2' + description: Issues or PRs related to the cozystack-api aggregated API server + +- name: area/ai + color: 'bfd4f2' + description: Issues or PRs related to AI agent guides, AGENTS.md, docs/agents/ + +- name: area/build + color: 'bfd4f2' + description: Issues or PRs related to image build infrastructure, multi-arch support + +- name: area/ci + color: 'bfd4f2' + description: Issues or PRs related to CI workflows, GitHub Actions, automation + +- name: area/dashboard + color: 'bfd4f2' + description: Issues or PRs related to the dashboard / UI + +- name: area/extra + color: 'bfd4f2' + description: Issues or PRs related to tenant-specific modules (packages/extra/) + +- name: area/database + color: 'bfd4f2' + description: Issues or PRs related to managed databases (postgres, mariadb, redis, etcd, kafka, clickhouse) + +- name: area/kubernetes + color: 'bfd4f2' + description: Issues or PRs related to the tenant Kubernetes app + +- name: area/monitoring + color: 'bfd4f2' + description: Issues or PRs related to the monitoring stack (vlogs, vmstack, grafana, workloadmonitor) + +- name: area/networking + color: 'bfd4f2' + description: Issues or PRs related to networking (ingress, gateway, vpn, metallb, cilium, kube-ovn) + +- name: area/platform + color: 'bfd4f2' + description: Issues or PRs related to platform infrastructure (bundle, flux, talos, installer) + +- name: area/release + color: 'bfd4f2' + description: Issues or PRs related to release tooling (changelog, backport, release pipeline) + +- name: area/storage + color: 'bfd4f2' + description: Issues or PRs related to storage (linstor, seaweedfs, bucket, velero, harbor) + +- name: area/testing + color: 'bfd4f2' + description: Issues or PRs related to testing (e2e, bats, unit tests) + +- name: area/virtualization + color: 'bfd4f2' + description: Issues or PRs related to virtualization (kubevirt, cdi, vmi, vm-import) + +- name: area/uncategorized + color: 'fbca04' + description: PR auto-labeler could not map title scope to a known area/*; please review + +# ────────────────────────────────────────────── +# do-not-merge/ — PR merge blockers (Prow convention) +# ────────────────────────────────────────────── + +- name: do-not-merge/work-in-progress + color: 'e11d21' + description: Indicates that a PR should not merge because it is a work in progress + # Both legacy spellings collapse here. EndBug processes aliases sequentially; + # the second rename hits a name collision and logs a warning — the legacy + # label survives and gets cleaned up in the follow-up dedup PR. + aliases: ['do-not-merge', 'do not merge'] + +- name: do-not-merge/hold + color: 'e11d21' + description: Indicates that a PR should not merge because someone has issued /hold + +# ────────────────────────────────────────────── +# Cozystack-specific (preserved) +# ────────────────────────────────────────────── + +- name: epic + color: 'a335ee' + description: A large development increment that brings definite value to Cozystack users + +- name: community + color: '97458a' + description: Community contributions are welcome in this issue + +- name: help wanted + color: '008672' + description: Extra attention is needed + +- name: good first issue + color: '7057ff' + description: Good for newcomers + +- name: quality-of-life + color: 'aaaaaa' + description: QoL improvements + +- name: upstream-issue + color: 'aaaaaa' + description: Requires resolving an issue in an upstream project + +- name: backport + color: 'fbca04' + description: Should change be backported on previous release + +- name: backport-previous + color: 'fbd876' + description: Backport target — previous release line + +- name: release + color: 'aaaaaa' + description: Releasing a new Cozystack version + +- name: automated + color: 'ededed' + description: Created by automation + +- name: debug + color: '704479' + description: Debugging in progress + +- name: sponsored + color: '00ff00' + description: Sponsored work + +- name: lgtm + color: '238636' + description: This PR has been approved by a maintainer + +- name: ok-to-test + color: '00ff00' + description: Indicates a non-member PR is safe to run CI on + +# ────────────────────────────────────────────── +# size: — PR size (auto-applied by sizing bot) +# ────────────────────────────────────────────── + +- name: 'size:XS' + color: '00ff00' + description: This PR changes 0-9 lines, ignoring generated files + +- name: 'size:S' + color: '77b800' + description: This PR changes 10-29 lines, ignoring generated files + +- name: 'size:M' + color: 'ebb800' + description: This PR changes 30-99 lines, ignoring generated files + +- name: 'size:L' + color: 'eb9500' + description: This PR changes 100-499 lines, ignoring generated files + +- name: 'size:XL' + color: 'ff823f' + description: This PR changes 500-999 lines, ignoring generated files + +- name: 'size:XXL' + color: 'ffb8b8' + description: This PR changes 1000+ lines, ignoring generated files + +# ────────────────────────────────────────────── +# security/ — security-finding severity and status +# ────────────────────────────────────────────── + +- name: security + color: 'aaaaaa' + description: Security-related issues and features + +- name: security/critical + color: 'd73a4a' + description: Critical security vulnerability + +- name: security/high + color: 'e99695' + description: High severity security finding + +- name: security/medium + color: 'f9c513' + description: Medium severity security finding + +- name: security/low + color: '0e8a16' + description: Low severity security finding + +- name: security/triage-needed + color: 'fbca04' + description: Needs security triage + +- name: security/confirmed + color: '1d76db' + description: Confirmed vulnerability + +- name: security/false-positive + color: 'c5def5' + description: Triaged as false positive + +- name: security/accepted-risk + color: 'bfd4f2' + description: Risk accepted with justification + +- name: security/in-progress + color: '0075ca' + description: Fix in progress + +- name: security/fixed + color: '0e8a16' + description: Fix released diff --git a/.github/workflows/auto-release.yaml b/.github/workflows/auto-release.yaml index 19d4b460..44ca8ca6 100644 --- a/.github/workflows/auto-release.yaml +++ b/.github/workflows/auto-release.yaml @@ -20,6 +20,14 @@ jobs: pull-requests: read steps: + - name: Generate GitHub App token + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.COZYSTACK_CI_APP_ID }} + private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }} + owner: cozystack + - name: Checkout code uses: actions/checkout@v4 with: @@ -28,27 +36,27 @@ jobs: - name: Configure git env: - GH_PAT: ${{ secrets.GH_PAT }} + APP_TOKEN: ${{ steps.app-token.outputs.token }} run: | - git config user.name "cozystack-bot" - git config user.email "217169706+cozystack-bot@users.noreply.github.com" - git remote set-url origin https://cozystack-bot:${GH_PAT}@github.com/${GITHUB_REPOSITORY} + git config user.name "cozystack-ci[bot]" + git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com" + git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY} git config --unset-all http.https://github.com/.extraheader || true - name: Process release branches uses: actions/github-script@v7 env: - GH_PAT: ${{ secrets.GH_PAT }} + APP_TOKEN: ${{ steps.app-token.outputs.token }} with: - github-token: ${{ secrets.GH_PAT }} + github-token: ${{ steps.app-token.outputs.token }} script: | const { execSync } = require('child_process'); - // Configure git to use PAT for authentication - execSync('git config user.name "cozystack-bot"', { encoding: 'utf8' }); - execSync('git config user.email "217169706+cozystack-bot@users.noreply.github.com"', { encoding: 'utf8' }); - execSync(`git remote set-url origin https://cozystack-bot:${process.env.GH_PAT}@github.com/${process.env.GITHUB_REPOSITORY}`, { encoding: 'utf8' }); - // Remove GITHUB_TOKEN extraheader to ensure PAT is used (needed to trigger other workflows) + // Configure git to use GitHub App token for authentication + execSync('git config user.name "cozystack-ci[bot]"', { encoding: 'utf8' }); + execSync('git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com"', { encoding: 'utf8' }); + execSync(`git remote set-url origin https://x-access-token:${process.env.APP_TOKEN}@github.com/${process.env.GITHUB_REPOSITORY}`, { encoding: 'utf8' }); + // Remove GITHUB_TOKEN extraheader to ensure App token is used (needed to trigger other workflows) execSync('git config --unset-all http.https://github.com/.extraheader || true', { encoding: 'utf8' }); // Get all release-X.Y branches diff --git a/.github/workflows/codegen-drift.yml b/.github/workflows/codegen-drift.yml new file mode 100644 index 00000000..a26caf5e --- /dev/null +++ b/.github/workflows/codegen-drift.yml @@ -0,0 +1,61 @@ +name: Codegen Drift Check + +on: + pull_request: + types: [opened, synchronize, reopened] + paths: + - 'api/**' + - 'pkg/apis/**' + - 'pkg/generated/**' + - 'internal/crdinstall/manifests/**' + - 'packages/system/cozystack-controller/definitions/**' + - 'packages/system/application-definition-crd/definition/**' + - 'packages/system/backup-controller/definitions/**' + - 'packages/system/backupstrategy-controller/definitions/**' + - 'hack/update-codegen.sh' + - 'hack/boilerplate.go.txt' + - 'Makefile' + - 'go.mod' + - 'go.sum' + - '.github/workflows/codegen-drift.yml' + +concurrency: + group: codegen-drift-${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + codegen-drift: + name: Verify generated code is up to date + runs-on: ubuntu-22.04 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: Pre-fetch k8s.io/code-generator module + # hack/update-codegen.sh sources kube_codegen.sh from the Go module cache. + # The module is not declared in go.mod, so fetch it explicitly at the + # version pinned in the script. + run: | + version=$(grep -oP 'code-generator@\Kv[0-9.]+' hack/update-codegen.sh) + tmpdir=$(mktemp -d) + cd "$tmpdir" + go mod init codegen-fetch + go get "k8s.io/code-generator@${version}" + + - name: Run make generate + run: make generate + + - name: Fail on drift + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "::error::'make generate' produced changes. Run 'make generate' locally and commit the result." + git status --short + git diff --color=always + exit 1 + fi diff --git a/.github/workflows/labels.yaml b/.github/workflows/labels.yaml new file mode 100644 index 00000000..69c18329 --- /dev/null +++ b/.github/workflows/labels.yaml @@ -0,0 +1,84 @@ +name: Labels + +on: + pull_request: + paths: + - .github/labels.yml + - .github/workflows/labels.yaml + push: + branches: [main] + paths: + - .github/labels.yml + - .github/workflows/labels.yaml + workflow_dispatch: + schedule: + - cron: '17 4 * * 1' # Mondays at 04:17 UTC + +permissions: + contents: read + +concurrency: + group: labels-sync + cancel-in-progress: false + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Validate labels.yml schema + run: | + python3 - <<'PY' + import re, sys, yaml + + path = '.github/labels.yml' + data = yaml.safe_load(open(path)) + errors = [] + + # 1. description ≤ 100 chars (GitHub REST API limit) + for label in data: + desc = label.get('description', '') or '' + if len(desc) > 100: + errors.append(f"{label['name']}: description {len(desc)} chars (max 100)") + + # 2. color is 6-char hex without leading # + for label in data: + color = label.get('color', '') or '' + if not re.match(r'^[0-9A-Fa-f]{6}$', color): + errors.append(f"{label['name']}: bad color {color!r} (must be 6-char hex without #)") + + # 3. unique top-level names + names = [label['name'] for label in data] + dups = sorted({n for n in names if names.count(n) > 1}) + for n in dups: + errors.append(f"duplicate name: {n}") + + # 4. aliases do not collide with any top-level name + name_set = set(names) + for label in data: + for alias in (label.get('aliases') or []): + if alias in name_set: + errors.append(f"alias {alias!r} (under {label['name']!r}) collides with a top-level name") + + if errors: + for err in errors: + print(f"::error::{err}") + sys.exit(1) + + print(f"labels.yml schema OK ({len(data)} labels)") + PY + + sync: + needs: validate + if: github.event_name != 'pull_request' + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: actions/checkout@v4 + - uses: EndBug/label-sync@v2 + with: + config-file: .github/labels.yml + delete-other-labels: false diff --git a/.github/workflows/pr-labeler.yaml b/.github/workflows/pr-labeler.yaml new file mode 100644 index 00000000..6e6f3017 --- /dev/null +++ b/.github/workflows/pr-labeler.yaml @@ -0,0 +1,206 @@ +name: PR Auto-Label + +on: + pull_request_target: + types: [opened, edited, reopened, synchronize] + +permissions: + contents: read + pull-requests: write + +jobs: + label: + runs-on: ubuntu-latest + steps: + - name: Apply labels from PR title + uses: actions/github-script@v7 + with: + script: | + // Conventional Commits types accepted by Cozystack (per docs/agents/contributing.md): + // feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert + // Mapping below maps a subset to kind/* — types not listed do not produce a kind/*. + const typeToKind = { + feat: 'kind/feature', + fix: 'kind/bug', + docs: 'kind/documentation', + chore: 'kind/cleanup', + refactor: 'kind/cleanup', + // style, perf, test, build, ci, revert — no kind mapping + }; + + // scope -> area/* mapping. Keys are the scopes observed in cozystack issues + // and PRs. Add new entries when a scope recurs (3+ times). + const scopeToArea = { + // area/api + 'api': 'area/api', + 'cozystack-api': 'area/api', + + // area/ai + 'agents': 'area/ai', + 'ai': 'area/ai', + + // area/build + 'build': 'area/build', + + // area/ci + 'ci': 'area/ci', + + // area/dashboard + 'dashboard': 'area/dashboard', + + // area/database + 'postgres': 'area/database', + 'postgres-operator': 'area/database', + 'mariadb': 'area/database', + 'mariadb-operator': 'area/database', + 'redis': 'area/database', + 'etcd': 'area/database', + 'kafka': 'area/database', + 'clickhouse': 'area/database', + + // area/extra + 'extra': 'area/extra', + + // area/kubernetes + 'kubernetes': 'area/kubernetes', + 'apps/kubernetes': 'area/kubernetes', + + // area/monitoring + 'monitoring': 'area/monitoring', + 'vlogs': 'area/monitoring', + 'vmstack': 'area/monitoring', + 'grafana': 'area/monitoring', + 'workloadmonitor': 'area/monitoring', + + // area/networking + 'ingress': 'area/networking', + 'ingress-nginx': 'area/networking', + 'gateway': 'area/networking', + 'vpn': 'area/networking', + 'metallb': 'area/networking', + 'cilium': 'area/networking', + 'kube-ovn': 'area/networking', + 'tcp-balancer': 'area/networking', + 'securitygroups': 'area/networking', + 'cozy-proxy': 'area/networking', + + // area/platform + 'platform': 'area/platform', + 'bundle': 'area/platform', + 'flux': 'area/platform', + 'fluxcd': 'area/platform', + 'cluster-api': 'area/platform', + 'talos': 'area/platform', + 'installer': 'area/platform', + 'cozyctl': 'area/platform', + 'cozystack-engine': 'area/platform', + 'cozy-lib': 'area/platform', + + // area/release + 'backport': 'area/release', + 'release': 'area/release', + + // area/storage + 'seaweedfs': 'area/storage', + 'seaweedfs-cosi-driver': 'area/storage', + 'bucket': 'area/storage', + 'linstor': 'area/storage', + 'velero': 'area/storage', + 'harbor': 'area/storage', + 'backups': 'area/storage', + + // area/testing + 'tests': 'area/testing', + 'e2e': 'area/testing', + + // area/virtualization + 'kubevirt': 'area/virtualization', + 'cdi': 'area/virtualization', + 'vmi': 'area/virtualization', + 'vm-import': 'area/virtualization', + 'virtual-machine': 'area/virtualization', + 'hami': 'area/virtualization', + 'gpu-operator': 'area/virtualization', + }; + + const pr = context.payload.pull_request; + const title = pr.title || ''; + const body = pr.body || ''; + const existing = new Set((pr.labels || []).map(l => l.name)); + const toAdd = new Set(); + + // 1. Strip "[Backport release-1.x]" prefix if present. + const backportMatch = title.match(/^\[Backport ([^\]]+)\]\s+(.+)$/); + const cleanTitle = backportMatch ? backportMatch[2] : title; + if (backportMatch) { + toAdd.add('area/release'); + toAdd.add('backport'); + } + + // 2. Try Conventional Commits form: type(scope)?(!)?: description + const conv = cleanTitle.match(/^([a-z]+)(?:\(([^)]+)\))?(!)?:\s*.+$/); + // 3. Fall back to bracket form: [scope] description + const bracket = !conv && cleanTitle.match(/^\[([^\]]+)\]\s+.+$/); + + let type = null, scopeStr = null, breaking = false; + if (conv) { + type = conv[1]; + scopeStr = conv[2] || null; + breaking = !!conv[3]; + } else if (bracket) { + scopeStr = bracket[1]; + } + + // 4. Detect BREAKING CHANGE: or BREAKING-CHANGE: footer in body. + // Conventional Commits 1.0 spec item 16 treats them as synonymous. + if (/^BREAKING[ -]CHANGE:/m.test(body)) { + breaking = true; + } + + // 5. Apply kind/* from type. + if (type) { + if (typeToKind[type]) { + toAdd.add(typeToKind[type]); + } else { + core.warning(`type "${type}" has no kind/* mapping — typo or new type? See .github/workflows/pr-labeler.yaml typeToKind`); + } + } + + // 6. Apply area/* from scope. Composite scopes split on comma. + const scopes = (scopeStr || '') + .split(/,\s*/) + .map(s => s.trim()) + .filter(Boolean); + for (const s of scopes) { + if (scopeToArea[s]) { + toAdd.add(scopeToArea[s]); + } else { + core.warning(`scope "${s}" has no area/* mapping — consider extending scopeToArea in .github/workflows/pr-labeler.yaml if it recurs`); + } + } + + // 7. kind/breaking-change. + if (breaking) { + toAdd.add('kind/breaking-change'); + } + + // 8. Fallback: no area/* applied -> area/uncategorized. + const hasArea = [...toAdd].some(l => l.startsWith('area/')); + if (!hasArea) { + toAdd.add('area/uncategorized'); + } + + // 9. Additive only — never remove existing labels. + const newLabels = [...toAdd].filter(l => !existing.has(l)); + if (newLabels.length === 0) { + core.info('No new labels to apply'); + return; + } + + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pr.number, + labels: newLabels, + }); + core.info(`Applied labels: ${newLabels.join(', ')}`); diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index ef1ce088..b2d82ff3 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -28,7 +28,7 @@ jobs: - name: Install generate run: | - curl -sSL https://github.com/cozystack/cozyvalues-gen/releases/download/v1.0.6/cozyvalues-gen-linux-amd64.tar.gz | tar -xzvf- -C /usr/local/bin/ cozyvalues-gen + curl -sSL https://github.com/cozystack/cozyvalues-gen/releases/download/v1.3.0/cozyvalues-gen-linux-amd64.tar.gz | tar -xzvf- -C /usr/local/bin/ cozyvalues-gen - name: Run pre-commit hooks run: | diff --git a/.github/workflows/pull-requests-release.yaml b/.github/workflows/pull-requests-release.yaml index 72f31b54..bb7f5da5 100644 --- a/.github/workflows/pull-requests-release.yaml +++ b/.github/workflows/pull-requests-release.yaml @@ -23,6 +23,14 @@ jobs: contains(github.event.pull_request.labels.*.name, 'release') steps: + - name: Generate GitHub App token + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.COZYSTACK_CI_APP_ID }} + private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }} + owner: cozystack + # Extract tag from branch name (branch = release-X.Y.Z*) - name: Extract tag from branch name id: get_tag @@ -47,11 +55,11 @@ jobs: - name: Create tag on merge commit env: - GH_PAT: ${{ secrets.GH_PAT }} + APP_TOKEN: ${{ steps.app-token.outputs.token }} run: | - git config user.name "cozystack-bot" - git config user.email "217169706+cozystack-bot@users.noreply.github.com" - git remote set-url origin https://cozystack-bot:${GH_PAT}@github.com/${GITHUB_REPOSITORY} + git config user.name "cozystack-ci[bot]" + git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com" + git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY} git tag -f ${{ steps.get_tag.outputs.tag }} ${{ github.sha }} git push -f origin ${{ steps.get_tag.outputs.tag }} @@ -59,7 +67,7 @@ jobs: - name: Ensure maintenance branch release-X.Y uses: actions/github-script@v7 with: - github-token: ${{ secrets.GH_PAT }} + github-token: ${{ steps.app-token.outputs.token }} script: | const tag = '${{ steps.get_tag.outputs.tag }}'; // e.g. v0.1.3 or v0.1.3-rc3 const match = tag.match(/^v(\d+)\.(\d+)\.\d+(?:[-\w\.]+)?$/); diff --git a/.github/workflows/pull-requests.yaml b/.github/workflows/pull-requests.yaml index b9b7b0ac..d9898616 100644 --- a/.github/workflows/pull-requests.yaml +++ b/.github/workflows/pull-requests.yaml @@ -6,8 +6,6 @@ env: on: pull_request: types: [opened, synchronize, reopened] - paths-ignore: - - 'docs/**/*' # Cancel in‑flight runs for the same PR when a new push arrives. concurrency: @@ -15,16 +13,32 @@ concurrency: cancel-in-progress: true jobs: + detect-changes: + name: Detect changes + runs-on: ubuntu-latest + outputs: + code: ${{ steps.filter.outputs.code }} + steps: + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + code: + - '!docs/**' + build: name: Build runs-on: [self-hosted] + timeout-minutes: 30 permissions: contents: read packages: write - # Never run when the PR carries the "release" label. + needs: ["detect-changes"] + # Never run when the PR carries the "release" label or only docs changed. if: | - !contains(github.event.pull_request.labels.*.name, 'release') + needs.detect-changes.outputs.code == 'true' + && !contains(github.event.pull_request.labels.*.name, 'release') steps: - name: Checkout code @@ -85,6 +99,14 @@ jobs: disk_id: ${{ steps.fetch_assets.outputs.disk_id }} steps: + - name: Generate GitHub App token + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.COZYSTACK_CI_APP_ID }} + private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }} + owner: cozystack + - name: Checkout code if: contains(github.event.pull_request.labels.*.name, 'release') uses: actions/checkout@v4 @@ -111,7 +133,7 @@ jobs: id: fetch_assets uses: actions/github-script@v7 with: - github-token: ${{ secrets.GH_PAT }} + github-token: ${{ steps.app-token.outputs.token }} script: | const tag = '${{ steps.get_tag.outputs.tag }}'; const releases = await github.rest.repos.listReleases({ @@ -137,6 +159,7 @@ jobs: name: "E2E Tests" runs-on: ${{ contains(github.event.pull_request.labels.*.name, 'debug') && 'self-hosted' || 'oracle-vm-24cpu-96gb-x86-64' }} #runs-on: [oracle-vm-32cpu-128gb-x86-64] + timeout-minutes: 120 permissions: contents: read packages: read @@ -144,6 +167,15 @@ jobs: if: ${{ always() && (needs.build.result == 'success' || needs.resolve_assets.result == 'success') }} steps: + - name: Generate GitHub App token + if: contains(github.event.pull_request.labels.*.name, 'release') + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.COZYSTACK_CI_APP_ID }} + private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }} + owner: cozystack + # ▸ Checkout and prepare the codebase - name: Checkout code uses: actions/checkout@v4 @@ -173,11 +205,11 @@ jobs: if: contains(github.event.pull_request.labels.*.name, 'release') run: | mkdir -p _out/assets - curl -sSL -H "Authorization: token ${GH_PAT}" -H "Accept: application/octet-stream" \ + curl -sSL -H "Authorization: token ${APP_TOKEN}" -H "Accept: application/octet-stream" \ -o _out/assets/nocloud-amd64.raw.xz \ "https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/assets/${{ needs.resolve_assets.outputs.disk_id }}" env: - GH_PAT: ${{ secrets.GH_PAT }} + APP_TOKEN: ${{ steps.app-token.outputs.token }} - name: Set sandbox ID run: echo "SANDBOX_NAME=cozy-e2e-sandbox-$(echo "${GITHUB_REPOSITORY}:${GITHUB_WORKFLOW}:${GITHUB_REF}" | sha256sum | cut -c1-10)" >> $GITHUB_ENV diff --git a/.github/workflows/tags.yaml b/.github/workflows/tags.yaml index ad9dd0a9..1b68ee32 100644 --- a/.github/workflows/tags.yaml +++ b/.github/workflows/tags.yaml @@ -16,6 +16,8 @@ jobs: prepare-release: name: Prepare Release runs-on: [self-hosted] + outputs: + skip: ${{ steps.check_release.outputs.skip }} permissions: contents: write packages: write @@ -23,6 +25,14 @@ jobs: actions: write steps: + - name: Generate GitHub App token + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.COZYSTACK_CI_APP_ID }} + private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }} + owner: cozystack + # Check if a non-draft release with this tag already exists - name: Check if release already exists id: check_release @@ -113,15 +123,29 @@ jobs: - name: Commit release artifacts if: steps.check_release.outputs.skip == 'false' env: - GH_PAT: ${{ secrets.GH_PAT }} + APP_TOKEN: ${{ steps.app-token.outputs.token }} run: | - git config user.name "cozystack-bot" - git config user.email "217169706+cozystack-bot@users.noreply.github.com" - git remote set-url origin https://cozystack-bot:${GH_PAT}@github.com/${GITHUB_REPOSITORY} + git config user.name "cozystack-ci[bot]" + git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com" + git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY} git config --unset-all http.https://github.com/.extraheader || true git add . git commit -m "Prepare release ${GITHUB_REF#refs/tags/}" -s || echo "No changes to commit" - git push origin HEAD || true + + # Tag the api/apps/v1alpha1 submodule for pkg.go.dev + - name: Tag API submodule + if: steps.check_release.outputs.skip == 'false' + env: + APP_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + VTAG="${{ steps.tag.outputs.tag }}" + SUBTAG="api/apps/v1alpha1/${VTAG}" + git config user.name "cozystack-ci[bot]" + git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com" + git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY} + TARGET="$(git rev-parse "${VTAG}^{}")" + git tag -f "${SUBTAG}" "$TARGET" + git push -f origin "refs/tags/${SUBTAG}" # Create or reuse draft release - name: Create / reuse draft release @@ -167,11 +191,11 @@ jobs: - name: Create release branch if: steps.check_release.outputs.skip == 'false' env: - GH_PAT: ${{ secrets.GH_PAT }} + APP_TOKEN: ${{ steps.app-token.outputs.token }} run: | - git config user.name "cozystack-bot" - git config user.email "217169706+cozystack-bot@users.noreply.github.com" - git remote set-url origin https://cozystack-bot:${GH_PAT}@github.com/${GITHUB_REPOSITORY} + git config user.name "cozystack-ci[bot]" + git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com" + git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY} BRANCH="release-${GITHUB_REF#refs/tags/v}" git branch -f "$BRANCH" git push -f origin "$BRANCH" @@ -181,7 +205,7 @@ jobs: if: steps.check_release.outputs.skip == 'false' uses: actions/github-script@v7 with: - github-token: ${{ secrets.GH_PAT }} + github-token: ${{ steps.app-token.outputs.token }} script: | const version = context.ref.replace('refs/tags/v', ''); const base = '${{ steps.get_base.outputs.branch }}'; @@ -199,7 +223,7 @@ jobs: repo: context.repo.repo, head, base, - title: `Release v${version}`, + title: `chore(release): cut v${version}`, body: `This PR prepares the release \`v${version}\`.`, draft: false }); @@ -223,6 +247,29 @@ jobs: pull-requests: write if: needs.prepare-release.result == 'success' steps: + - name: Generate GitHub App token + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.COZYSTACK_CI_APP_ID }} + private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }} + owner: cozystack + + # Read-only token for the AI step. Minting a separate scoped token + # means the Generate changelog using AI step cannot push branches, + # open PRs, or mutate any repository even with --allow-all-tools, + # regardless of whether the agent follows the prompt's instructions. + - name: Generate read-only GitHub App token + id: app-token-read + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.COZYSTACK_CI_APP_ID }} + private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }} + owner: cozystack + permission-contents: read + permission-pull-requests: read + permission-metadata: read + - name: Parse tag id: tag uses: actions/github-script@v7 @@ -245,7 +292,7 @@ jobs: ref: main fetch-depth: 0 fetch-tags: true - token: ${{ secrets.GH_PAT }} + token: ${{ steps.app-token.outputs.token }} - name: Check if changelog already exists id: check_changelog @@ -271,56 +318,65 @@ jobs: - name: Generate changelog using AI if: steps.check_changelog.outputs.exists == 'false' + timeout-minutes: 30 env: COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - GH_TOKEN: ${{ secrets.GH_PAT }} + GH_TOKEN: ${{ steps.app-token-read.outputs.token }} + VERSION: ${{ steps.tag.outputs.version }} run: | - copilot --prompt "prepare changelog file for tagged release v${{ steps.tag.outputs.version }}, use @docs/agents/changelog.md for it. Create the changelog file at docs/changelogs/v${{ steps.tag.outputs.version }}.md" \ + copilot \ + --prompt "Generate the release changelog for tag v${VERSION}. Follow the instructions in @docs/agents/changelog.md exactly, including the 'Scope and boundaries' section at the top. Your deliverable is the single file docs/changelogs/v${VERSION}.md — write it and exit; this workflow handles branching, committing, pushing, and opening the PR." \ --allow-all-tools --allow-all-paths < /dev/null - name: Create changelog branch and commit if: steps.check_changelog.outputs.exists == 'false' env: - GH_PAT: ${{ secrets.GH_PAT }} + APP_TOKEN: ${{ steps.app-token.outputs.token }} + VERSION: ${{ steps.tag.outputs.version }} run: | - git config user.name "cozystack-bot" - git config user.email "217169706+cozystack-bot@users.noreply.github.com" - git remote set-url origin https://cozystack-bot:${GH_PAT}@github.com/${GITHUB_REPOSITORY} - - CHANGELOG_FILE="docs/changelogs/v${{ steps.tag.outputs.version }}.md" - CHANGELOG_BRANCH="changelog-v${{ steps.tag.outputs.version }}" - - if [ -f "$CHANGELOG_FILE" ]; then - # Fetch latest main branch - git fetch origin main - - # Delete local branch if it exists - git branch -D "$CHANGELOG_BRANCH" 2>/dev/null || true - - # Create and checkout new branch from main - git checkout -b "$CHANGELOG_BRANCH" origin/main - - # Add and commit changelog - git add "$CHANGELOG_FILE" - if git diff --staged --quiet; then - echo "⚠️ No changes to commit (file may already be committed)" - else - git commit -m "docs: add changelog for v${{ steps.tag.outputs.version }}" -s - echo "✅ Changelog committed to branch $CHANGELOG_BRANCH" - fi - - # Push the branch (force push to update if it exists) - git push -f origin "$CHANGELOG_BRANCH" - else - echo "⚠️ Changelog file was not generated" + set -euo pipefail + + CHANGELOG_FILE="docs/changelogs/v${VERSION}.md" + CHANGELOG_BRANCH="changelog-v${VERSION}" + + if [ ! -f "$CHANGELOG_FILE" ]; then + echo "::error::Changelog file $CHANGELOG_FILE was not produced by the Generate changelog using AI step" exit 1 fi + if [ ! -s "$CHANGELOG_FILE" ]; then + echo "::error::Changelog file $CHANGELOG_FILE is empty" + exit 1 + fi + + # Snapshot the file across the branch switch — the checkout below + # resets tracked files to match origin/main. + TEMP_FILE="$(mktemp)" + trap 'rm -f "$TEMP_FILE"' EXIT + cp "$CHANGELOG_FILE" "$TEMP_FILE" + + git config user.name "cozystack-ci[bot]" + git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com" + git remote set-url origin "https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY}" + + git fetch origin main + git branch -D "$CHANGELOG_BRANCH" 2>/dev/null || true + git checkout -b "$CHANGELOG_BRANCH" origin/main + + mkdir -p "$(dirname "$CHANGELOG_FILE")" + cp "$TEMP_FILE" "$CHANGELOG_FILE" + + # The `check_changelog` step gated this job on the file being absent + # from origin/main, so `git add` + `git commit` must produce a diff. + # If they don't, something is wrong (e.g. empty file) — fail loud. + git add "$CHANGELOG_FILE" + git commit -m "docs: add changelog for v${VERSION}" -s + git push -f origin "$CHANGELOG_BRANCH" - name: Create PR for changelog if: steps.check_changelog.outputs.exists == 'false' uses: actions/github-script@v7 with: - github-token: ${{ secrets.GH_PAT }} + github-token: ${{ steps.app-token.outputs.token }} script: | const version = '${{ steps.tag.outputs.version }}'; const changelogBranch = `changelog-v${version}`; @@ -355,7 +411,7 @@ jobs: repo: context.repo.repo, head: changelogBranch, base: baseBranch, - title: `docs: add changelog for v${version}`, + title: `docs(release): add changelog for v${version}`, body: `This PR adds the changelog for release \`v${version}\`.\n\n✅ Changelog has been automatically generated in \`docs/changelogs/v${version}.md\`.`, draft: false }); @@ -365,8 +421,128 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, issue_number: pr.data.number, - labels: ['documentation', 'automated'] + labels: ['kind/documentation', 'automated'] }); console.log(`Created PR #${pr.data.number} for changelog`); } + + update-website-docs: + name: Update Website Docs + runs-on: [self-hosted] + needs: [generate-changelog, prepare-release] + if: needs.generate-changelog.result == 'success' && needs.prepare-release.outputs.skip != 'true' + permissions: + contents: read + steps: + - name: Generate GitHub App token + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.COZYSTACK_CI_APP_ID }} + private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }} + owner: cozystack + + - name: Parse tag + id: tag + uses: actions/github-script@v7 + with: + script: | + const ref = context.ref.replace('refs/tags/', ''); + const m = ref.match(/^v(\d+\.\d+\.\d+)(-(?:alpha|beta|rc)\.\d+)?$/); + if (!m) { + core.setFailed(`❌ tag '${ref}' must match 'vX.Y.Z' or 'vX.Y.Z-(alpha|beta|rc).N'`); + return; + } + const version = m[1] + (m[2] ?? ''); + core.setOutput('tag', ref); // v0.22.0 + core.setOutput('version', version); // 0.22.0 + + - name: Checkout website repo + uses: actions/checkout@v4 + with: + repository: cozystack/website + token: ${{ steps.app-token.outputs.token }} + ref: main + + # Decide whether this release promotes the `next/` trunk to a new released + # version directory. Per the website repo's contract (see website#495): + # - `make release-next` runs only for new minor/major final releases + # (tag matches `vX.Y.Z` with no prerelease suffix AND `vX.Y/` does + # not yet exist on disk). + # - Prereleases and patch releases skip this step and let + # `make update-all` handle routing on its own. + - name: Determine if this release promotes next/ + id: promote + env: + TAG: ${{ steps.tag.outputs.tag }} + run: | + if [[ ! "$TAG" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then + echo "promote=false" >> "$GITHUB_OUTPUT" + echo "Prerelease tag '$TAG' — skipping release-next." + exit 0 + fi + MAJOR="${BASH_REMATCH[1]}" + MINOR="${BASH_REMATCH[2]}" + if [[ "$MAJOR" == "0" ]]; then + DOC_VERSION="v0" + else + DOC_VERSION="v${MAJOR}.${MINOR}" + fi + if [[ -d "content/en/docs/${DOC_VERSION}" ]]; then + echo "promote=false" >> "$GITHUB_OUTPUT" + echo "content/en/docs/${DOC_VERSION}/ already exists — patch release, skipping release-next." + else + echo "promote=true" >> "$GITHUB_OUTPUT" + echo "New minor/major release ${DOC_VERSION} — will promote next/ → ${DOC_VERSION}/." + fi + + - name: Promote next/ to released version + if: steps.promote.outputs.promote == 'true' + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + TAG: ${{ steps.tag.outputs.tag }} + run: make release-next RELEASE_TAG="$TAG" + + - name: Update docs from release branch + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + TAG: ${{ steps.tag.outputs.tag }} + VERSION: ${{ steps.tag.outputs.version }} + run: make update-all BRANCH="release-$VERSION" RELEASE_TAG="$TAG" + + - name: Commit and push + id: commit + run: | + git config user.name "cozystack-ci[bot]" + git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com" + git add content hugo.yaml + if git diff --cached --quiet; then + echo "No changes to commit" + echo "changed=false" >> $GITHUB_OUTPUT + exit 0 + fi + BRANCH="update-docs-v${{ steps.tag.outputs.version }}" + git branch -D "$BRANCH" 2>/dev/null || true + git checkout -b "$BRANCH" + git commit --signoff -m "[docs] Update managed apps reference for v${{ steps.tag.outputs.version }}" + git push --force --set-upstream origin "$BRANCH" + echo "changed=true" >> $GITHUB_OUTPUT + + - name: Open pull request + if: steps.commit.outputs.changed == 'true' + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + BRANCH="update-docs-v${{ steps.tag.outputs.version }}" + pr_state=$(gh pr view "$BRANCH" --repo cozystack/website --json state --jq .state 2>/dev/null || echo "") + if [[ "$pr_state" == "OPEN" ]]; then + echo "PR already open, skipping creation." + else + gh pr create \ + --repo cozystack/website \ + --title "[docs] Update managed apps reference for v${{ steps.tag.outputs.version }}" \ + --body "Automated docs update for release \`v${{ steps.tag.outputs.version }}\`." \ + --head "update-docs-v${{ steps.tag.outputs.version }}" \ + --base main + fi diff --git a/.gitignore b/.gitignore index 0ecfa223..61003de5 100644 --- a/.gitignore +++ b/.gitignore @@ -80,3 +80,7 @@ fabric.properties **/.DS_Store tmp/ + +# build revision marker (generated by make image-packages) +packages/core/platform/.build-revision +.claude/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ac0f7e30..6492b92d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,17 @@ repos: - repo: local hooks: + - id: run-make-generate-root + name: Run 'make generate' at repo root + entry: | + flock -x .git/pre-commit.lock sh -c ' + echo "Running make generate at repo root" + make generate || exit $? + git diff --color=always | cat + ' + language: system + files: ^(api/|pkg/apis/|pkg/generated/|internal/crdinstall/manifests/|packages/system/cozystack-controller/definitions/|packages/system/application-definition-crd/definition/|packages/system/backup-controller/definitions/|packages/system/backupstrategy-controller/definitions/|hack/update-codegen\.sh$|hack/boilerplate\.go\.txt$|Makefile$) + pass_filenames: false - id: run-make-generate name: Run 'make generate' in all app directories entry: | diff --git a/AGENTS.md b/AGENTS.md index eb1febf7..1e9c4d15 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,6 +27,12 @@ working with the **Cozystack** project. - Read: [`contributing.md`](./docs/agents/contributing.md) - Action: Read the file to understand git workflow, commit format, PR process +- **Issue and PR labeling, triage** (e.g., "label this issue", "what label should I use", "triage this", "categorize") + - Read: [`.github/labels.yml`](./.github/labels.yml) + - Action: Use labels defined there. Conventions follow the Kubernetes scheme — `kind/*` (type), `area/*` (subsystem), `priority/*` (urgency), `triage/*` (review state), `lifecycle/*` (auto-close), `do-not-merge/*` (PR blockers), `security/*` (severity) + - For `area/*`: accuracy outweighs reuse. If no existing `area/*` truly fits the change, propose a new one via PR (extend `.github/labels.yml` and the scope mapping in `.github/workflows/pr-labeler.yaml`) — do not shoehorn the change into a wrong area. `area/uncategorized` is the auto-labeler fallback; treat it as a signal to pick a fit, create a new area, or correct the PR title + - PR titles: a Conventional Commits header (`type(scope): description`, types from [`contributing.md`](./docs/agents/contributing.md)) auto-applies `kind/*` and `area/*` via `.github/workflows/pr-labeler.yaml`. Append `!` (or add a `BREAKING CHANGE:` footer) to apply `kind/breaking-change` + **Important rules:** - ✅ **ONLY read the file if the task matches the documented process scope** - do not read files for tasks that don't match their purpose - ✅ **ALWAYS read the file FIRST** before starting the task (when applicable) @@ -53,7 +59,7 @@ working with the **Cozystack** project. ### Conventions - **Helm Charts**: Umbrella pattern, vendored upstream charts in `charts/` - **Go Code**: Controller-runtime patterns, kubebuilder style -- **Git Commits**: `[component] Description` format with `--signoff` +- **Git Commits**: Conventional Commits (`type(scope): description`) with `--signoff` ### What NOT to Do - ❌ Edit `/vendor/`, `zz_generated.*.go`, upstream charts directly diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 9c44daab..d7061941 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -10,3 +10,5 @@ | Timur Tukaev | [@tym83](https://github.com/tym83) | Ænix | Cozystack Website, Marketing, Community Management | | Kirill Klinchenkov | [@klinch0](https://github.com/klinch0) | Ænix | Core Maintainer | | Nikita Bykov | [@nbykov0](https://github.com/nbykov0) | Ænix | Maintainer of ARM and stuff | +| Matthieu Robin | [@matthieu-robin](https://github.com/matthieu-robin) | Hidora | Managed Applications, Platform Quality & Benchmarking | +| Mattia Eleuteri | [@mattia-eleuteri](https://github.com/mattia-eleuteri) | Hidora | CSI, Storage, Networking & Security | diff --git a/Makefile b/Makefile index 73bd0bea..acc9b6f6 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: manifests assets unit-tests helm-unit-tests verify-crds +.PHONY: manifests assets unit-tests helm-unit-tests bats-unit-tests preflight include hack/common-envs.mk @@ -22,11 +22,13 @@ build: build-deps make -C packages/system/lineage-controller-webhook image make -C packages/system/cilium image make -C packages/system/linstor image + make -C packages/system/linstor-gui image make -C packages/system/kubeovn-webhook image make -C packages/system/kubeovn-plunger image make -C packages/system/dashboard image make -C packages/system/metallb image make -C packages/system/kamaji image + make -C packages/system/multus image make -C packages/system/bucket image make -C packages/system/objectstorage-controller image make -C packages/system/grafana-operator image @@ -38,30 +40,31 @@ build: build-deps manifests: mkdir -p _out/assets - cat packages/core/installer/crds/*.yaml > _out/assets/cozystack-crds.yaml + cat internal/crdinstall/manifests/*.yaml > _out/assets/cozystack-crds.yaml # Talos variant (default) helm template installer packages/core/installer -n cozy-system \ - -s templates/cozystack-operator.yaml \ - -s templates/packagesource.yaml \ + --show-only templates/cozystack-operator.yaml \ > _out/assets/cozystack-operator-talos.yaml # Generic Kubernetes variant (k3s, kubeadm, RKE2) helm template installer packages/core/installer -n cozy-system \ --set cozystackOperator.variant=generic \ --set cozystack.apiServerHost=REPLACE_ME \ - -s templates/cozystack-operator.yaml \ - -s templates/packagesource.yaml \ + --show-only templates/cozystack-operator.yaml \ > _out/assets/cozystack-operator-generic.yaml # Hosted variant (managed Kubernetes) helm template installer packages/core/installer -n cozy-system \ --set cozystackOperator.variant=hosted \ - -s templates/cozystack-operator.yaml \ - -s templates/packagesource.yaml \ + --show-only templates/cozystack-operator.yaml \ > _out/assets/cozystack-operator-hosted.yaml cozypkg: go build -ldflags "-X github.com/cozystack/cozystack/cmd/cozypkg/cmd.Version=v$(COZYSTACK_VERSION)" -o _out/bin/cozypkg ./cmd/cozypkg -assets: assets-talos assets-cozypkg +assets: assets-talos assets-cozypkg openapi-json + +openapi-json: + mkdir -p _out/assets + VERSION=$(shell git describe --tags --always 2>/dev/null || echo dev) go run ./tools/openapi-gen/ 2>/dev/null > _out/assets/openapi.json assets-talos: make -C packages/core/talos assets @@ -80,15 +83,46 @@ test: make -C packages/core/testing apply make -C packages/core/testing test -verify-crds: - @diff --recursive packages/core/installer/crds/ internal/crdinstall/manifests/ --exclude='.*' \ - || (echo "ERROR: CRD manifests out of sync. Run 'make generate' to fix." && exit 1) - -unit-tests: helm-unit-tests verify-crds +unit-tests: helm-unit-tests bats-unit-tests go-unit-tests helm-unit-tests: hack/helm-unit-tests.sh +# Scoped go test over the cozystack-api surface that this repo owns. Kept +# narrow intentionally - running `go test ./...` pulls in generated code +# round-trip suites whose behavior depends on tool versions outside this +# repo's control (kubebuilder, openapi-gen, etc.) and is better exercised +# from their generator workflows. +go-unit-tests: + go test ./pkg/registry/... ./pkg/config/... ./pkg/cmd/server/... + +# Discover every hack/*.bats file that is NOT an e2e test and run it +# through cozytest.sh. Drop a new *.bats file in hack/ and it is picked +# up automatically on the next `make unit-tests` run. +# +# Caveat: $(wildcard ...) returns space-separated names, so a filename +# containing a literal space would split into multiple tokens here. All +# current bats files use hyphen-separated names; if the project ever +# introduces whitespace-bearing filenames this recipe must be rewritten +# (e.g. to use `find ... -print0 | xargs -0`). +BATS_UNIT_FILES := $(filter-out hack/e2e-%.bats,$(wildcard hack/*.bats)) + +bats-unit-tests: + @if [ -z "$(BATS_UNIT_FILES)" ]; then \ + echo "ERROR: no hack/*.bats unit test files found"; \ + exit 1; \ + fi + @for f in $(BATS_UNIT_FILES); do \ + echo "--- running $$f ---"; \ + hack/cozytest.sh "$$f" || exit 1; \ + done + +# Operator-facing host preflight check. Warns about a standalone +# containerd.service or docker.service running alongside the embedded +# k3s runtime. Safe to run at any time; always exits 0. +preflight: + @hack/check-host-runtime.sh + prepare-env: make -C packages/core/testing apply make -C packages/core/testing prepare-cluster diff --git a/README.md b/README.md index b81af2a7..5bc8d7be 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,12 @@ [![Support](https://img.shields.io/badge/$-support-12a0df.svg?style=flat)](https://cozystack.io/support/) [![Active](http://img.shields.io/badge/Status-Active-green.svg)](https://github.com/cozystack/cozystack) [![GitHub Release](https://img.shields.io/github/release/cozystack/cozystack.svg?style=flat)](https://github.com/cozystack/cozystack/releases/latest) -[![GitHub Commit](https://img.shields.io/github/commit-activity/y/cozystack/cozystack)](https://github.com/cozystack/cozystack/graphs/contributors) +[![GitHub Commit](https://img.shields.io/github/commit-activity/y/cozystack/cozystack)](https://github.com/cozystack/cozystack/graphs/contributors) +[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/10177/badge)](https://www.bestpractices.dev/projects/10177) # Cozystack -**Cozystack** is a free PaaS platform and framework for building clouds. +**Cozystack** is a free platform and framework for building clouds. Cozystack is a [CNCF Sandbox Level Project](https://www.cncf.io/sandbox-projects/) that was originally built and sponsored by [Ænix](https://aenix.io/). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..b64e533b --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,102 @@ +# Security Policy + +## Scope + +This policy applies to the [`cozystack/cozystack`](https://github.com/cozystack/cozystack) repository and to release artifacts produced from it, including Cozystack core components, operators, packaged manifests, container images, and installation assets published by the project. + +Cozystack integrates and ships many upstream cloud native components. If you believe a vulnerability originates in an upstream project rather than in Cozystack-specific code, packaging, defaults, or integration logic, please report it to the upstream project as well. If you are unsure, report it to Cozystack first and we will help route or coordinate the issue. + +## Supported Versions + +As of March 17, 2026, the Cozystack project maintains multiple release lines. Security fixes are prioritized for the latest stable release line and, when needed, backported to other supported lines. + +| Version line | Status | Notes | +| --- | --- | --- | +| `v1.1.x` | Supported | Current stable release line. | +| `v1.0.x` | Supported | Previous stable release line; receives security and important maintenance fixes. | +| `v0.41.x` | Limited support | Legacy pre-v1 line during the v0 to v1 transition; critical security and upgrade-blocking fixes may be backported at maintainer discretion. | +| `< v0.41` | Not supported | Please upgrade to a supported release line before requesting a security fix. | +| `alpha`, `beta`, `rc` releases | Not supported | Pre-release builds are for testing and evaluation only. | + +Supported versions may change over time as new release lines are cut. The authoritative source for current releases is the GitHub Releases page: + + + +## Reporting a Vulnerability + +Please do **not** report security vulnerabilities through public GitHub issues, discussions, pull requests, Telegram, Slack, or other public community channels. + +At the moment, this repository does not publish a dedicated private security mailbox in-tree. If you need to report a vulnerability: + +1. Contact one of the project maintainers listed in `CODEOWNERS` using an existing private channel you already have. +2. If you do not already have a private maintainer contact, use a public community channel only to request a private contact path, without disclosing any vulnerability details. + +Please do not include exploit details, credentials, tokens, private keys, customer data, or other sensitive material in any public message. + +When reporting a vulnerability, please include as much of the following as possible: + +- affected Cozystack version, tag, or commit +- affected component or package, for example operator, API server, dashboard, installer, or a packaged system component +- deployment environment and provider, for example bare metal, Hetzner, Oracle Cloud, or other infrastructure +- prerequisites and exact reproduction steps +- impact, attack scenario, and expected blast radius +- whether authentication, tenant access, cluster-admin access, or network adjacency is required +- known mitigations or workarounds +- whether you believe the issue also affects an upstream dependency + +## What to Expect + +The maintainers will aim to: + +- acknowledge receipt within 3 business days +- perform an initial triage and severity assessment within 7 business days +- keep the reporter informed as the fix and disclosure plan are developed + +Resolution timelines depend on severity, complexity, release branch applicability, and whether coordination with upstream projects is required. + +## Disclosure Process + +The Cozystack project follows a coordinated disclosure model. + +- We ask reporters to keep details private until a fix or mitigation is available and users have had a reasonable opportunity to upgrade. +- When appropriate, maintainers may use GitHub Security Advisories or equivalent coordinated disclosure tooling to manage remediation and public disclosure. +- If appropriate, the project may request or publish a GHSA and/or CVE as part of the disclosure process. +- Fixes will normally be released in the supported version lines affected by the issue, subject to severity and feasibility. + +Public disclosure will typically happen through one or more of the following: + +- GitHub Releases and release notes +- project changelogs and documentation updates +- GitHub Security Advisories, when used for coordinated disclosure + +## Project Security Practices + +Security is part of the normal Cozystack development and release process. Current project practices include: + +- maintainer-owned review through pull requests and `CODEOWNERS` +- automated pull request checks, including pre-commit validation, unit tests, builds, and end-to-end testing +- release automation with patch releases, release branches, and backport workflows +- ongoing maintenance of packaged dependencies and platform integrations across supported release lines + +Because Cozystack is an integration-heavy platform, some vulnerabilities may require coordination across multiple repositories or with upstream maintainers before a public fix can be released. + +## Security Fixes and Announcements + +Security fixes are published in normal release artifacts whenever possible. Users should monitor: + +- GitHub Releases: +- project changelogs in this repository +- the Cozystack website and documentation: + +## Out of Scope + +The following are generally out of scope for private security reporting unless there is a clear Cozystack-specific impact: + +- vulnerabilities in unsupported or end-of-life Cozystack versions +- issues that require access already equivalent to cluster-admin, node root, or direct infrastructure administrator privileges, unless they bypass an expected Cozystack security boundary +- vulnerabilities that exist only in an upstream dependency and are not introduced or materially worsened by Cozystack packaging, configuration, or defaults +- requests for security best-practice advice without a concrete vulnerability + +## Credits + +We appreciate responsible disclosure and will credit reporters in public advisories or release notes unless anonymous disclosure is requested. diff --git a/api/apps/v1alpha1/bucket/types.go b/api/apps/v1alpha1/bucket/types.go new file mode 100644 index 00000000..96082a67 --- /dev/null +++ b/api/apps/v1alpha1/bucket/types.go @@ -0,0 +1,33 @@ +// Code generated by values-gen. DO NOT EDIT. +// +kubebuilder:object:generate=true +// +groupName=apps.cozystack.io +// +versionName=v1alpha1 +package bucket + +import ( + "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +type Config struct { + v1.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + Spec ConfigSpec `json:"spec,omitempty"` +} + +type ConfigSpec struct { + // Provisions bucket from the `-lock` BucketClass (with object lock enabled). + // +kubebuilder:default:=false + Locking bool `json:"locking"` + // Selects a specific BucketClass by storage pool name. + // +kubebuilder:default:="" + StoragePool string `json:"storagePool,omitempty"` + // Users configuration map. + // +kubebuilder:default:={} + Users map[string]User `json:"users,omitempty"` +} + +type User struct { + // Whether the user has read-only access. + Readonly bool `json:"readonly,omitempty"` +} diff --git a/api/apps/v1alpha1/bucket/zz_generated.deepcopy.go b/api/apps/v1alpha1/bucket/zz_generated.deepcopy.go new file mode 100644 index 00000000..26f925cd --- /dev/null +++ b/api/apps/v1alpha1/bucket/zz_generated.deepcopy.go @@ -0,0 +1,88 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package bucket + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Config) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { + *out = *in + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make(map[string]User, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. +func (in *ConfigSpec) DeepCopy() *ConfigSpec { + if in == nil { + return nil + } + out := new(ConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *User) DeepCopyInto(out *User) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new User. +func (in *User) DeepCopy() *User { + if in == nil { + return nil + } + out := new(User) + in.DeepCopyInto(out) + return out +} diff --git a/api/apps/v1alpha1/clickhouse/types.go b/api/apps/v1alpha1/clickhouse/types.go new file mode 100644 index 00000000..153ff5e0 --- /dev/null +++ b/api/apps/v1alpha1/clickhouse/types.go @@ -0,0 +1,112 @@ +// Code generated by values-gen. DO NOT EDIT. +// +kubebuilder:object:generate=true +// +groupName=apps.cozystack.io +// +versionName=v1alpha1 +package clickhouse + +import ( + resource "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +type Config struct { + v1.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + Spec ConfigSpec `json:"spec,omitempty"` +} + +type ConfigSpec struct { + // Number of ClickHouse replicas. + // +kubebuilder:default:=2 + Replicas int `json:"replicas"` + // Number of ClickHouse shards. + // +kubebuilder:default:=1 + Shards int `json:"shards"` + // Explicit CPU and memory configuration for each ClickHouse replica. When omitted, the preset defined in `resourcesPreset` is applied. + // +kubebuilder:default:={} + Resources Resources `json:"resources,omitempty"` + // Default sizing preset used when `resources` is omitted. + // +kubebuilder:default:="small" + ResourcesPreset ResourcesPreset `json:"resourcesPreset"` + // Persistent Volume Claim size available for application data. + // +kubebuilder:default:="10Gi" + Size resource.Quantity `json:"size"` + // StorageClass used to store the data. + // +kubebuilder:default:="" + StorageClass string `json:"storageClass"` + // Size of Persistent Volume for logs. + // +kubebuilder:default:="2Gi" + LogStorageSize resource.Quantity `json:"logStorageSize"` + // TTL (expiration time) for `query_log` and `query_thread_log`. + // +kubebuilder:default:=15 + LogTTL int `json:"logTTL"` + // Users configuration map. + // +kubebuilder:default:={} + Users map[string]User `json:"users,omitempty"` + // Backup configuration. + // +kubebuilder:default:={} + Backup Backup `json:"backup"` + // ClickHouse Keeper configuration. + // +kubebuilder:default:={} + ClickhouseKeeper ClickHouseKeeper `json:"clickhouseKeeper"` +} + +type Backup struct { + // Retention strategy for cleaning up old backups. + // +kubebuilder:default:="--keep-last=3 --keep-daily=3 --keep-within-weekly=1m" + CleanupStrategy string `json:"cleanupStrategy"` + // Enable regular backups (default: false). + // +kubebuilder:default:=false + Enabled bool `json:"enabled"` + // Password for Restic backup encryption. + // +kubebuilder:default:="" + ResticPassword string `json:"resticPassword"` + // Access key for S3 authentication. + // +kubebuilder:default:="" + S3AccessKey string `json:"s3AccessKey"` + // S3 bucket used for storing backups. + // +kubebuilder:default:="s3.example.org/clickhouse-backups" + S3Bucket string `json:"s3Bucket"` + // AWS S3 region where backups are stored. + // +kubebuilder:default:="us-east-1" + S3Region string `json:"s3Region"` + // Secret key for S3 authentication. + // +kubebuilder:default:="" + S3SecretKey string `json:"s3SecretKey"` + // Cron schedule for automated backups. + // +kubebuilder:default:="0 2 * * *" + Schedule string `json:"schedule"` +} + +type ClickHouseKeeper struct { + // Deploy ClickHouse Keeper for cluster coordination. + // +kubebuilder:default:=true + Enabled bool `json:"enabled,omitempty"` + // Number of Keeper replicas. + // +kubebuilder:default:=3 + Replicas int `json:"replicas,omitempty"` + // Default sizing preset. + // +kubebuilder:default:="micro" + ResourcesPreset ResourcesPreset `json:"resourcesPreset,omitempty"` + // Persistent Volume Claim size available for application data. + // +kubebuilder:default:="1Gi" + Size resource.Quantity `json:"size,omitempty"` +} + +type Resources struct { + // CPU available to each replica. + Cpu resource.Quantity `json:"cpu,omitempty"` + // Memory (RAM) available to each replica. + Memory resource.Quantity `json:"memory,omitempty"` +} + +type User struct { + // Password for the user. + Password string `json:"password,omitempty"` + // User is readonly (default: false). + Readonly bool `json:"readonly,omitempty"` +} + +// +kubebuilder:validation:Enum="nano";"micro";"small";"medium";"large";"xlarge";"2xlarge" +type ResourcesPreset string diff --git a/api/apps/v1alpha1/clickhouse/zz_generated.deepcopy.go b/api/apps/v1alpha1/clickhouse/zz_generated.deepcopy.go new file mode 100644 index 00000000..2c3a6ed8 --- /dev/null +++ b/api/apps/v1alpha1/clickhouse/zz_generated.deepcopy.go @@ -0,0 +1,141 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package clickhouse + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Backup) DeepCopyInto(out *Backup) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Backup. +func (in *Backup) DeepCopy() *Backup { + if in == nil { + return nil + } + out := new(Backup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClickHouseKeeper) DeepCopyInto(out *ClickHouseKeeper) { + *out = *in + out.Size = in.Size.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClickHouseKeeper. +func (in *ClickHouseKeeper) DeepCopy() *ClickHouseKeeper { + if in == nil { + return nil + } + out := new(ClickHouseKeeper) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Config) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) + out.Size = in.Size.DeepCopy() + out.LogStorageSize = in.LogStorageSize.DeepCopy() + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make(map[string]User, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + out.Backup = in.Backup + in.ClickhouseKeeper.DeepCopyInto(&out.ClickhouseKeeper) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. +func (in *ConfigSpec) DeepCopy() *ConfigSpec { + if in == nil { + return nil + } + out := new(ConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + out.Cpu = in.Cpu.DeepCopy() + out.Memory = in.Memory.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *User) DeepCopyInto(out *User) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new User. +func (in *User) DeepCopy() *User { + if in == nil { + return nil + } + out := new(User) + in.DeepCopyInto(out) + return out +} diff --git a/api/apps/v1alpha1/foundationdb/types.go b/api/apps/v1alpha1/foundationdb/types.go new file mode 100644 index 00000000..5b0e5920 --- /dev/null +++ b/api/apps/v1alpha1/foundationdb/types.go @@ -0,0 +1,162 @@ +// Code generated by values-gen. DO NOT EDIT. +// +kubebuilder:object:generate=true +// +groupName=apps.cozystack.io +// +versionName=v1alpha1 +package foundationdb + +import ( + resource "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +type Config struct { + v1.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + Spec ConfigSpec `json:"spec,omitempty"` +} + +type ConfigSpec struct { + // Cluster configuration. + // +kubebuilder:default:={} + Cluster Cluster `json:"cluster"` + // Storage configuration. + // +kubebuilder:default:={} + Storage Storage `json:"storage"` + // Explicit CPU and memory configuration for each FoundationDB instance. When omitted, the preset defined in `resourcesPreset` is applied. + // +kubebuilder:default:={} + Resources Resources `json:"resources,omitempty"` + // Default sizing preset used when `resources` is omitted. + // +kubebuilder:default:="medium" + ResourcesPreset ResourcesPreset `json:"resourcesPreset"` + // Backup configuration. + // +kubebuilder:default:={} + Backup Backup `json:"backup"` + // Monitoring configuration. + // +kubebuilder:default:={} + Monitoring Monitoring `json:"monitoring"` + // Custom parameters to pass to FoundationDB. + // +kubebuilder:default:={} + CustomParameters []string `json:"customParameters,omitempty"` + // Container image deployment type. + // +kubebuilder:default:="unified" + ImageType ImageType `json:"imageType"` + // Security context for containers. + // +kubebuilder:default:={} + SecurityContext SecurityContext `json:"securityContext"` + // Enable automatic pod replacements. + // +kubebuilder:default:=true + AutomaticReplacements bool `json:"automaticReplacements"` +} + +type Backup struct { + // Enable backups. + // +kubebuilder:default:=false + Enabled bool `json:"enabled"` + // Retention policy for backups. + // +kubebuilder:default:="7d" + RetentionPolicy string `json:"retentionPolicy"` + // S3 configuration for backups. + // +kubebuilder:default:={} + S3 BackupS3 `json:"s3"` +} + +type BackupS3 struct { + // S3 bucket name. + // +kubebuilder:default:="" + Bucket string `json:"bucket"` + // S3 credentials. + // +kubebuilder:default:={} + Credentials BackupS3Credentials `json:"credentials"` + // S3 endpoint URL. + // +kubebuilder:default:="" + Endpoint string `json:"endpoint"` + // S3 region. + // +kubebuilder:default:="us-east-1" + Region string `json:"region"` +} + +type BackupS3Credentials struct { + // S3 access key ID. + // +kubebuilder:default:="" + AccessKeyId string `json:"accessKeyId"` + // S3 secret access key. + // +kubebuilder:default:="" + SecretAccessKey string `json:"secretAccessKey"` +} + +type Cluster struct { + // Fault domain configuration. + // +kubebuilder:default:={} + FaultDomain ClusterFaultDomain `json:"faultDomain"` + // Process counts for different roles. + // +kubebuilder:default:={} + ProcessCounts ClusterProcessCounts `json:"processCounts"` + // Database redundancy mode (single, double, triple, three_datacenter, three_datacenter_fallback). + // +kubebuilder:default:="double" + RedundancyMode string `json:"redundancyMode"` + // Storage engine (ssd-2, ssd-redwood-v1, ssd-rocksdb-v1, memory). + // +kubebuilder:default:="ssd-2" + StorageEngine string `json:"storageEngine"` + // Version of FoundationDB to use. + // +kubebuilder:default:="7.3.63" + Version string `json:"version"` +} + +type ClusterFaultDomain struct { + // Fault domain key. + // +kubebuilder:default:="kubernetes.io/hostname" + Key string `json:"key"` + // Fault domain value source. + // +kubebuilder:default:="spec.nodeName" + ValueFrom string `json:"valueFrom"` +} + +type ClusterProcessCounts struct { + // Number of cluster controller processes. + // +kubebuilder:default:=1 + ClusterController int `json:"cluster_controller"` + // Number of stateless processes (-1 for automatic). + // +kubebuilder:default:=-1 + Stateless int `json:"stateless"` + // Number of storage processes (determines cluster size). + // +kubebuilder:default:=3 + Storage int `json:"storage"` +} + +type Monitoring struct { + // Enable WorkloadMonitor integration. + // +kubebuilder:default:=true + Enabled bool `json:"enabled"` +} + +type Resources struct { + // CPU available to each instance. + Cpu resource.Quantity `json:"cpu,omitempty"` + // Memory (RAM) available to each instance. + Memory resource.Quantity `json:"memory,omitempty"` +} + +type SecurityContext struct { + // Group ID to run the container. + // +kubebuilder:default:=4059 + RunAsGroup int `json:"runAsGroup"` + // User ID to run the container. + // +kubebuilder:default:=4059 + RunAsUser int `json:"runAsUser"` +} + +type Storage struct { + // Size of persistent volumes for each instance. + // +kubebuilder:default:="16Gi" + Size resource.Quantity `json:"size"` + // Storage class (if not set, uses cluster default). + // +kubebuilder:default:="" + StorageClass string `json:"storageClass"` +} + +// +kubebuilder:validation:Enum="unified";"split" +type ImageType string + +// +kubebuilder:validation:Enum="small";"medium";"large";"xlarge";"2xlarge" +type ResourcesPreset string diff --git a/api/apps/v1alpha1/foundationdb/zz_generated.deepcopy.go b/api/apps/v1alpha1/foundationdb/zz_generated.deepcopy.go new file mode 100644 index 00000000..92d80421 --- /dev/null +++ b/api/apps/v1alpha1/foundationdb/zz_generated.deepcopy.go @@ -0,0 +1,234 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package foundationdb + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Backup) DeepCopyInto(out *Backup) { + *out = *in + out.S3 = in.S3 +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Backup. +func (in *Backup) DeepCopy() *Backup { + if in == nil { + return nil + } + out := new(Backup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupS3) DeepCopyInto(out *BackupS3) { + *out = *in + out.Credentials = in.Credentials +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupS3. +func (in *BackupS3) DeepCopy() *BackupS3 { + if in == nil { + return nil + } + out := new(BackupS3) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupS3Credentials) DeepCopyInto(out *BackupS3Credentials) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupS3Credentials. +func (in *BackupS3Credentials) DeepCopy() *BackupS3Credentials { + if in == nil { + return nil + } + out := new(BackupS3Credentials) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Cluster) DeepCopyInto(out *Cluster) { + *out = *in + out.FaultDomain = in.FaultDomain + out.ProcessCounts = in.ProcessCounts +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Cluster. +func (in *Cluster) DeepCopy() *Cluster { + if in == nil { + return nil + } + out := new(Cluster) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterFaultDomain) DeepCopyInto(out *ClusterFaultDomain) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterFaultDomain. +func (in *ClusterFaultDomain) DeepCopy() *ClusterFaultDomain { + if in == nil { + return nil + } + out := new(ClusterFaultDomain) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterProcessCounts) DeepCopyInto(out *ClusterProcessCounts) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterProcessCounts. +func (in *ClusterProcessCounts) DeepCopy() *ClusterProcessCounts { + if in == nil { + return nil + } + out := new(ClusterProcessCounts) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Config) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { + *out = *in + out.Cluster = in.Cluster + in.Storage.DeepCopyInto(&out.Storage) + in.Resources.DeepCopyInto(&out.Resources) + out.Backup = in.Backup + out.Monitoring = in.Monitoring + if in.CustomParameters != nil { + in, out := &in.CustomParameters, &out.CustomParameters + *out = make([]string, len(*in)) + copy(*out, *in) + } + out.SecurityContext = in.SecurityContext +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. +func (in *ConfigSpec) DeepCopy() *ConfigSpec { + if in == nil { + return nil + } + out := new(ConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Monitoring) DeepCopyInto(out *Monitoring) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Monitoring. +func (in *Monitoring) DeepCopy() *Monitoring { + if in == nil { + return nil + } + out := new(Monitoring) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + out.Cpu = in.Cpu.DeepCopy() + out.Memory = in.Memory.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecurityContext) DeepCopyInto(out *SecurityContext) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecurityContext. +func (in *SecurityContext) DeepCopy() *SecurityContext { + if in == nil { + return nil + } + out := new(SecurityContext) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Storage) DeepCopyInto(out *Storage) { + *out = *in + out.Size = in.Size.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Storage. +func (in *Storage) DeepCopy() *Storage { + if in == nil { + return nil + } + out := new(Storage) + in.DeepCopyInto(out) + return out +} diff --git a/api/apps/v1alpha1/go.mod b/api/apps/v1alpha1/go.mod new file mode 100644 index 00000000..d5d57383 --- /dev/null +++ b/api/apps/v1alpha1/go.mod @@ -0,0 +1,24 @@ +module github.com/cozystack/cozystack/api/apps/v1alpha1 + +go 1.25.0 + +require k8s.io/apimachinery v0.35.2 + +require ( + github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect + github.com/x448/float16 v0.8.4 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + golang.org/x/net v0.47.0 // indirect + golang.org/x/text v0.31.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect + k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect +) diff --git a/api/apps/v1alpha1/go.sum b/api/apps/v1alpha1/go.sum new file mode 100644 index 00000000..d9b1cf31 --- /dev/null +++ b/api/apps/v1alpha1/go.sum @@ -0,0 +1,56 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/apimachinery v0.35.2 h1:NqsM/mmZA7sHW02JZ9RTtk3wInRgbVxL8MPfzSANAK8= +k8s.io/apimachinery v0.35.2/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= +k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/api/apps/v1alpha1/harbor/types.go b/api/apps/v1alpha1/harbor/types.go new file mode 100644 index 00000000..974576b3 --- /dev/null +++ b/api/apps/v1alpha1/harbor/types.go @@ -0,0 +1,114 @@ +// Code generated by values-gen. DO NOT EDIT. +// +kubebuilder:object:generate=true +// +groupName=apps.cozystack.io +// +versionName=v1alpha1 +package harbor + +import ( + resource "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +type Config struct { + v1.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + Spec ConfigSpec `json:"spec,omitempty"` +} + +type ConfigSpec struct { + // Hostname for external access to Harbor (defaults to 'harbor' subdomain for the tenant host). + // +kubebuilder:default:="" + Host string `json:"host,omitempty"` + // StorageClass used to store the data. + // +kubebuilder:default:="" + StorageClass string `json:"storageClass"` + // Core API server configuration. + // +kubebuilder:default:={} + Core Core `json:"core"` + // Container image registry configuration. + // +kubebuilder:default:={} + Registry Registry `json:"registry"` + // Background job service configuration. + // +kubebuilder:default:={} + Jobservice Jobservice `json:"jobservice"` + // Trivy vulnerability scanner configuration. + // +kubebuilder:default:={} + Trivy Trivy `json:"trivy"` + // PostgreSQL database configuration. + // +kubebuilder:default:={} + Database Database `json:"database"` + // Redis cache configuration. + // +kubebuilder:default:={} + Redis Redis `json:"redis"` +} + +type Core struct { + // Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. + // +kubebuilder:default:={} + Resources Resources `json:"resources,omitempty"` + // Default sizing preset used when `resources` is omitted. + // +kubebuilder:default:="small" + ResourcesPreset ResourcesPreset `json:"resourcesPreset,omitempty"` +} + +type Database struct { + // Number of database instances. + // +kubebuilder:default:=2 + Replicas int `json:"replicas"` + // Persistent Volume size for database storage. + // +kubebuilder:default:="5Gi" + Size resource.Quantity `json:"size"` +} + +type Jobservice struct { + // Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. + // +kubebuilder:default:={} + Resources Resources `json:"resources,omitempty"` + // Default sizing preset used when `resources` is omitted. + // +kubebuilder:default:="nano" + ResourcesPreset ResourcesPreset `json:"resourcesPreset,omitempty"` +} + +type Redis struct { + // Number of Redis replicas. + // +kubebuilder:default:=2 + Replicas int `json:"replicas"` + // Persistent Volume size for cache storage. + // +kubebuilder:default:="1Gi" + Size resource.Quantity `json:"size"` +} + +type Registry struct { + // Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. + // +kubebuilder:default:={} + Resources Resources `json:"resources,omitempty"` + // Default sizing preset used when `resources` is omitted. + // +kubebuilder:default:="small" + ResourcesPreset ResourcesPreset `json:"resourcesPreset,omitempty"` +} + +type Resources struct { + // Number of CPU cores allocated. + Cpu resource.Quantity `json:"cpu,omitempty"` + // Amount of memory allocated. + Memory resource.Quantity `json:"memory,omitempty"` +} + +type Trivy struct { + // Enable or disable the vulnerability scanner. + // +kubebuilder:default:=true + Enabled bool `json:"enabled"` + // Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. + // +kubebuilder:default:={} + Resources Resources `json:"resources,omitempty"` + // Default sizing preset used when `resources` is omitted. + // +kubebuilder:default:="nano" + ResourcesPreset ResourcesPreset `json:"resourcesPreset,omitempty"` + // Persistent Volume size for vulnerability database cache. + // +kubebuilder:default:="5Gi" + Size resource.Quantity `json:"size"` +} + +// +kubebuilder:validation:Enum="nano";"micro";"small";"medium";"large";"xlarge";"2xlarge" +type ResourcesPreset string diff --git a/api/apps/v1alpha1/harbor/zz_generated.deepcopy.go b/api/apps/v1alpha1/harbor/zz_generated.deepcopy.go new file mode 100644 index 00000000..3e7338ad --- /dev/null +++ b/api/apps/v1alpha1/harbor/zz_generated.deepcopy.go @@ -0,0 +1,186 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package harbor + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Config) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { + *out = *in + in.Core.DeepCopyInto(&out.Core) + in.Registry.DeepCopyInto(&out.Registry) + in.Jobservice.DeepCopyInto(&out.Jobservice) + in.Trivy.DeepCopyInto(&out.Trivy) + in.Database.DeepCopyInto(&out.Database) + in.Redis.DeepCopyInto(&out.Redis) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. +func (in *ConfigSpec) DeepCopy() *ConfigSpec { + if in == nil { + return nil + } + out := new(ConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Core) DeepCopyInto(out *Core) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Core. +func (in *Core) DeepCopy() *Core { + if in == nil { + return nil + } + out := new(Core) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Database) DeepCopyInto(out *Database) { + *out = *in + out.Size = in.Size.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Database. +func (in *Database) DeepCopy() *Database { + if in == nil { + return nil + } + out := new(Database) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Jobservice) DeepCopyInto(out *Jobservice) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Jobservice. +func (in *Jobservice) DeepCopy() *Jobservice { + if in == nil { + return nil + } + out := new(Jobservice) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Redis) DeepCopyInto(out *Redis) { + *out = *in + out.Size = in.Size.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Redis. +func (in *Redis) DeepCopy() *Redis { + if in == nil { + return nil + } + out := new(Redis) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Registry) DeepCopyInto(out *Registry) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Registry. +func (in *Registry) DeepCopy() *Registry { + if in == nil { + return nil + } + out := new(Registry) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + out.Cpu = in.Cpu.DeepCopy() + out.Memory = in.Memory.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Trivy) DeepCopyInto(out *Trivy) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) + out.Size = in.Size.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Trivy. +func (in *Trivy) DeepCopy() *Trivy { + if in == nil { + return nil + } + out := new(Trivy) + in.DeepCopyInto(out) + return out +} diff --git a/api/apps/v1alpha1/httpcache/types.go b/api/apps/v1alpha1/httpcache/types.go new file mode 100644 index 00000000..30bb10ba --- /dev/null +++ b/api/apps/v1alpha1/httpcache/types.go @@ -0,0 +1,72 @@ +// Code generated by values-gen. DO NOT EDIT. +// +kubebuilder:object:generate=true +// +groupName=apps.cozystack.io +// +versionName=v1alpha1 +package httpcache + +import ( + resource "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +type Config struct { + v1.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + Spec ConfigSpec `json:"spec,omitempty"` +} + +type ConfigSpec struct { + // Persistent Volume Claim size available for application data. + // +kubebuilder:default:="10Gi" + Size resource.Quantity `json:"size"` + // StorageClass used to store the data. + // +kubebuilder:default:="" + StorageClass string `json:"storageClass"` + // Enable external access from outside the cluster. + // +kubebuilder:default:=false + External bool `json:"external"` + // Endpoints configuration, as a list of . + // +kubebuilder:default:={} + Endpoints []string `json:"endpoints,omitempty"` + // HAProxy configuration. + // +kubebuilder:default:={} + Haproxy HAProxy `json:"haproxy"` + // Nginx configuration. + // +kubebuilder:default:={} + Nginx Nginx `json:"nginx"` +} + +type HAProxy struct { + // Number of HAProxy replicas. + // +kubebuilder:default:=2 + Replicas int `json:"replicas"` + // Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. + // +kubebuilder:default:={} + Resources Resources `json:"resources,omitempty"` + // Default sizing preset used when `resources` is omitted. + // +kubebuilder:default:="nano" + ResourcesPreset ResourcesPreset `json:"resourcesPreset"` +} + +type Nginx struct { + // Number of Nginx replicas. + // +kubebuilder:default:=2 + Replicas int `json:"replicas"` + // Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. + // +kubebuilder:default:={} + Resources Resources `json:"resources,omitempty"` + // Default sizing preset used when `resources` is omitted. + // +kubebuilder:default:="nano" + ResourcesPreset ResourcesPreset `json:"resourcesPreset"` +} + +type Resources struct { + // CPU available to each replica. + Cpu resource.Quantity `json:"cpu,omitempty"` + // Memory (RAM) available to each replica. + Memory resource.Quantity `json:"memory,omitempty"` +} + +// +kubebuilder:validation:Enum="nano";"micro";"small";"medium";"large";"xlarge";"2xlarge" +type ResourcesPreset string diff --git a/api/apps/v1alpha1/httpcache/zz_generated.deepcopy.go b/api/apps/v1alpha1/httpcache/zz_generated.deepcopy.go new file mode 100644 index 00000000..47da2baa --- /dev/null +++ b/api/apps/v1alpha1/httpcache/zz_generated.deepcopy.go @@ -0,0 +1,123 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package httpcache + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Config) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { + *out = *in + out.Size = in.Size.DeepCopy() + if in.Endpoints != nil { + in, out := &in.Endpoints, &out.Endpoints + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.Haproxy.DeepCopyInto(&out.Haproxy) + in.Nginx.DeepCopyInto(&out.Nginx) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. +func (in *ConfigSpec) DeepCopy() *ConfigSpec { + if in == nil { + return nil + } + out := new(ConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HAProxy) DeepCopyInto(out *HAProxy) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HAProxy. +func (in *HAProxy) DeepCopy() *HAProxy { + if in == nil { + return nil + } + out := new(HAProxy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Nginx) DeepCopyInto(out *Nginx) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Nginx. +func (in *Nginx) DeepCopy() *Nginx { + if in == nil { + return nil + } + out := new(Nginx) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + out.Cpu = in.Cpu.DeepCopy() + out.Memory = in.Memory.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} diff --git a/api/apps/v1alpha1/kafka/types.go b/api/apps/v1alpha1/kafka/types.go new file mode 100644 index 00000000..485f34ee --- /dev/null +++ b/api/apps/v1alpha1/kafka/types.go @@ -0,0 +1,90 @@ +// Code generated by values-gen. DO NOT EDIT. +// +kubebuilder:object:generate=true +// +groupName=apps.cozystack.io +// +versionName=v1alpha1 +package kafka + +import ( + resource "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/apis/meta/v1" + k8sRuntime "k8s.io/apimachinery/pkg/runtime" +) + +// +kubebuilder:object:root=true +type Config struct { + v1.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + Spec ConfigSpec `json:"spec,omitempty"` +} + +type ConfigSpec struct { + // Enable external access from outside the cluster. + // +kubebuilder:default:=false + External bool `json:"external"` + // Topics configuration. + // +kubebuilder:default:={} + Topics []Topic `json:"topics,omitempty"` + // Kafka configuration. + // +kubebuilder:default:={} + Kafka Kafka `json:"kafka"` + // ZooKeeper configuration. + // +kubebuilder:default:={} + Zookeeper ZooKeeper `json:"zookeeper"` +} + +type Kafka struct { + // Number of Kafka replicas. + // +kubebuilder:default:=3 + Replicas int `json:"replicas"` + // Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. + // +kubebuilder:default:={} + Resources Resources `json:"resources,omitempty"` + // Default sizing preset used when `resources` is omitted. + // +kubebuilder:default:="small" + ResourcesPreset ResourcesPreset `json:"resourcesPreset"` + // Persistent Volume size for Kafka. + // +kubebuilder:default:="10Gi" + Size resource.Quantity `json:"size"` + // StorageClass used to store the Kafka data. + // +kubebuilder:default:="" + StorageClass string `json:"storageClass"` +} + +type Resources struct { + // CPU available to each replica. + Cpu resource.Quantity `json:"cpu,omitempty"` + // Memory (RAM) available to each replica. + Memory resource.Quantity `json:"memory,omitempty"` +} + +type Topic struct { + // Topic configuration. + Config k8sRuntime.RawExtension `json:"config"` + // Topic name. + Name string `json:"name"` + // Number of partitions. + Partitions int `json:"partitions"` + // Number of replicas. + Replicas int `json:"replicas"` +} + +type ZooKeeper struct { + // Number of ZooKeeper replicas. + // +kubebuilder:default:=3 + Replicas int `json:"replicas"` + // Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. + // +kubebuilder:default:={} + Resources Resources `json:"resources,omitempty"` + // Default sizing preset used when `resources` is omitted. + // +kubebuilder:default:="small" + ResourcesPreset ResourcesPreset `json:"resourcesPreset"` + // Persistent Volume size for ZooKeeper. + // +kubebuilder:default:="5Gi" + Size resource.Quantity `json:"size"` + // StorageClass used to store the ZooKeeper data. + // +kubebuilder:default:="" + StorageClass string `json:"storageClass"` +} + +// +kubebuilder:validation:Enum="nano";"micro";"small";"medium";"large";"xlarge";"2xlarge" +type ResourcesPreset string diff --git a/api/apps/v1alpha1/kafka/zz_generated.deepcopy.go b/api/apps/v1alpha1/kafka/zz_generated.deepcopy.go new file mode 100644 index 00000000..c2d39077 --- /dev/null +++ b/api/apps/v1alpha1/kafka/zz_generated.deepcopy.go @@ -0,0 +1,142 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package kafka + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Config) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { + *out = *in + if in.Topics != nil { + in, out := &in.Topics, &out.Topics + *out = make([]Topic, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Kafka.DeepCopyInto(&out.Kafka) + in.Zookeeper.DeepCopyInto(&out.Zookeeper) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. +func (in *ConfigSpec) DeepCopy() *ConfigSpec { + if in == nil { + return nil + } + out := new(ConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Kafka) DeepCopyInto(out *Kafka) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) + out.Size = in.Size.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Kafka. +func (in *Kafka) DeepCopy() *Kafka { + if in == nil { + return nil + } + out := new(Kafka) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + out.Cpu = in.Cpu.DeepCopy() + out.Memory = in.Memory.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Topic) DeepCopyInto(out *Topic) { + *out = *in + in.Config.DeepCopyInto(&out.Config) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Topic. +func (in *Topic) DeepCopy() *Topic { + if in == nil { + return nil + } + out := new(Topic) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ZooKeeper) DeepCopyInto(out *ZooKeeper) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) + out.Size = in.Size.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ZooKeeper. +func (in *ZooKeeper) DeepCopy() *ZooKeeper { + if in == nil { + return nil + } + out := new(ZooKeeper) + in.DeepCopyInto(out) + return out +} diff --git a/api/apps/v1alpha1/kubernetes/types.go b/api/apps/v1alpha1/kubernetes/types.go new file mode 100644 index 00000000..88a3c97e --- /dev/null +++ b/api/apps/v1alpha1/kubernetes/types.go @@ -0,0 +1,279 @@ +// Code generated by values-gen. DO NOT EDIT. +// +kubebuilder:object:generate=true +// +groupName=apps.cozystack.io +// +versionName=v1alpha1 +package kubernetes + +import ( + resource "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/apis/meta/v1" + k8sRuntime "k8s.io/apimachinery/pkg/runtime" +) + +// +kubebuilder:object:root=true +type Config struct { + v1.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + Spec ConfigSpec `json:"spec,omitempty"` +} + +type ConfigSpec struct { + // StorageClass used to store the data. + // +kubebuilder:default:="replicated" + StorageClass string `json:"storageClass"` + // Worker nodes configuration map. + // +kubebuilder:default:={"md0":{"ephemeralStorage":"20Gi","gpus":{},"instanceType":"u1.medium","maxReplicas":10,"minReplicas":0,"resources":{},"roles":{"ingress-nginx"}}} + NodeGroups map[string]NodeGroup `json:"nodeGroups,omitempty"` + // Kubernetes major.minor version to deploy + // +kubebuilder:default:="v1.35" + Version Version `json:"version"` + // External hostname for Kubernetes cluster. Defaults to `.` if empty. + // +kubebuilder:default:="" + Host string `json:"host"` + // Cluster addons configuration. + // +kubebuilder:default:={} + Addons Addons `json:"addons"` + // Kubernetes control-plane configuration. + // +kubebuilder:default:={} + ControlPlane ControlPlane `json:"controlPlane"` + // Optional image overrides for air-gapped or rate-limited registries. + // +kubebuilder:default:={} + Images Images `json:"images"` +} + +type APIServer struct { + // CPU and memory resources for API Server. + // +kubebuilder:default:={} + Resources Resources `json:"resources"` + // Preset if `resources` omitted. + // +kubebuilder:default:="large" + ResourcesPreset ResourcesPreset `json:"resourcesPreset"` +} + +type Addons struct { + // Cert-manager addon. + // +kubebuilder:default:={} + CertManager CertManagerAddon `json:"certManager"` + // Cilium CNI plugin. + // +kubebuilder:default:={} + Cilium CiliumAddon `json:"cilium"` + // CoreDNS addon. + // +kubebuilder:default:={} + Coredns CoreDNSAddon `json:"coredns"` + // FluxCD GitOps operator. + // +kubebuilder:default:={} + Fluxcd FluxCDAddon `json:"fluxcd"` + // Gateway API addon. + // +kubebuilder:default:={} + GatewayAPI GatewayAPIAddon `json:"gatewayAPI"` + // NVIDIA GPU Operator. + // +kubebuilder:default:={} + GpuOperator GPUOperatorAddon `json:"gpuOperator"` + // HAMi GPU virtualization middleware. + // +kubebuilder:default:={} + Hami HAMiAddon `json:"hami"` + // Ingress-NGINX controller. + // +kubebuilder:default:={} + IngressNginx IngressNginxAddon `json:"ingressNginx"` + // Monitoring agents. + // +kubebuilder:default:={} + MonitoringAgents MonitoringAgentsAddon `json:"monitoringAgents"` + // Velero backup/restore addon. + // +kubebuilder:default:={} + Velero VeleroAddon `json:"velero"` + // Vertical Pod Autoscaler. + // +kubebuilder:default:={} + VerticalPodAutoscaler VerticalPodAutoscalerAddon `json:"verticalPodAutoscaler"` +} + +type CertManagerAddon struct { + // Enable cert-manager. + // +kubebuilder:default:=false + Enabled bool `json:"enabled"` + // Custom Helm values overrides. + // +kubebuilder:default:={} + ValuesOverride k8sRuntime.RawExtension `json:"valuesOverride"` +} + +type CiliumAddon struct { + // Custom Helm values overrides. + // +kubebuilder:default:={} + ValuesOverride k8sRuntime.RawExtension `json:"valuesOverride"` +} + +type ControlPlane struct { + // API Server configuration. + // +kubebuilder:default:={} + ApiServer APIServer `json:"apiServer"` + // Controller Manager configuration. + // +kubebuilder:default:={} + ControllerManager ControllerManager `json:"controllerManager"` + // Konnectivity configuration. + // +kubebuilder:default:={} + Konnectivity Konnectivity `json:"konnectivity"` + // Number of control-plane replicas. + // +kubebuilder:default:=2 + Replicas int `json:"replicas"` + // Scheduler configuration. + // +kubebuilder:default:={} + Scheduler Scheduler `json:"scheduler"` +} + +type ControllerManager struct { + // CPU and memory resources for Controller Manager. + // +kubebuilder:default:={} + Resources Resources `json:"resources"` + // Preset if `resources` omitted. + // +kubebuilder:default:="micro" + ResourcesPreset ResourcesPreset `json:"resourcesPreset"` +} + +type CoreDNSAddon struct { + // Custom Helm values overrides. + // +kubebuilder:default:={} + ValuesOverride k8sRuntime.RawExtension `json:"valuesOverride"` +} + +type FluxCDAddon struct { + // Enable FluxCD. + // +kubebuilder:default:=false + Enabled bool `json:"enabled"` + // Custom Helm values overrides. + // +kubebuilder:default:={} + ValuesOverride k8sRuntime.RawExtension `json:"valuesOverride"` +} + +type GPU struct { + // Name of GPU, such as "nvidia.com/AD102GL_L40S". + Name string `json:"name"` +} + +type GPUOperatorAddon struct { + // Enable GPU Operator. + // +kubebuilder:default:=false + Enabled bool `json:"enabled"` + // Custom Helm values overrides. + // +kubebuilder:default:={} + ValuesOverride k8sRuntime.RawExtension `json:"valuesOverride"` +} + +type GatewayAPIAddon struct { + // Enable Gateway API. + // +kubebuilder:default:=false + Enabled bool `json:"enabled"` +} + +type HAMiAddon struct { + // Enable HAMi (requires GPU Operator). + // +kubebuilder:default:=false + Enabled bool `json:"enabled"` + // Custom Helm values overrides. + // +kubebuilder:default:={} + ValuesOverride k8sRuntime.RawExtension `json:"valuesOverride"` +} + +type Images struct { + // Image used by the wait-for-kubeconfig init container. Empty falls back to images/busybox.tag. + // +kubebuilder:default:="" + WaitForKubeconfig string `json:"waitForKubeconfig,omitempty"` +} + +type IngressNginxAddon struct { + // Enable the controller (requires nodes labeled `ingress-nginx`). + // +kubebuilder:default:=false + Enabled bool `json:"enabled"` + // Method to expose the controller. Allowed values: `Proxied`, `LoadBalancer`. + // +kubebuilder:default:="Proxied" + ExposeMethod IngressNginxExposeMethod `json:"exposeMethod"` + // Domains routed to this tenant cluster when `exposeMethod` is `Proxied`. + // +kubebuilder:default:={} + Hosts []string `json:"hosts,omitempty"` + // Custom Helm values overrides. + // +kubebuilder:default:={} + ValuesOverride k8sRuntime.RawExtension `json:"valuesOverride"` +} + +type Konnectivity struct { + // Konnectivity Server configuration. + // +kubebuilder:default:={} + Server KonnectivityServer `json:"server"` +} + +type KonnectivityServer struct { + // CPU and memory resources for Konnectivity. + // +kubebuilder:default:={} + Resources Resources `json:"resources"` + // Preset if `resources` omitted. + // +kubebuilder:default:="micro" + ResourcesPreset ResourcesPreset `json:"resourcesPreset"` +} + +type MonitoringAgentsAddon struct { + // Enable monitoring agents. + // +kubebuilder:default:=false + Enabled bool `json:"enabled"` + // Custom Helm values overrides. + // +kubebuilder:default:={} + ValuesOverride k8sRuntime.RawExtension `json:"valuesOverride"` +} + +type NodeGroup struct { + // Ephemeral storage size. + // +kubebuilder:default:="20Gi" + EphemeralStorage resource.Quantity `json:"ephemeralStorage"` + // List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM). + Gpus []GPU `json:"gpus,omitempty"` + // Virtual machine instance type. + // +kubebuilder:default:="u1.medium" + InstanceType string `json:"instanceType"` + // Maximum number of replicas. + // +kubebuilder:default:=10 + MaxReplicas int `json:"maxReplicas"` + // Minimum number of replicas. + // +kubebuilder:default:=0 + MinReplicas int `json:"minReplicas"` + // CPU and memory resources for each worker node. + Resources Resources `json:"resources"` + // List of node roles. + Roles []string `json:"roles,omitempty"` +} + +type Resources struct { + // CPU available. + Cpu resource.Quantity `json:"cpu,omitempty"` + // Memory (RAM) available. + Memory resource.Quantity `json:"memory,omitempty"` +} + +type Scheduler struct { + // CPU and memory resources for Scheduler. + // +kubebuilder:default:={} + Resources Resources `json:"resources"` + // Preset if `resources` omitted. + // +kubebuilder:default:="micro" + ResourcesPreset ResourcesPreset `json:"resourcesPreset"` +} + +type VeleroAddon struct { + // Enable Velero. + // +kubebuilder:default:=false + Enabled bool `json:"enabled"` + // Custom Helm values overrides. + // +kubebuilder:default:={} + ValuesOverride k8sRuntime.RawExtension `json:"valuesOverride"` +} + +type VerticalPodAutoscalerAddon struct { + // Custom Helm values overrides. + // +kubebuilder:default:={} + ValuesOverride k8sRuntime.RawExtension `json:"valuesOverride"` +} + +// +kubebuilder:validation:Enum="Proxied";"LoadBalancer" +type IngressNginxExposeMethod string + +// +kubebuilder:validation:Enum="nano";"micro";"small";"medium";"large";"xlarge";"2xlarge" +type ResourcesPreset string + +// +kubebuilder:validation:Enum="v1.35";"v1.34";"v1.33";"v1.32";"v1.31";"v1.30" +type Version string diff --git a/api/apps/v1alpha1/kubernetes/zz_generated.deepcopy.go b/api/apps/v1alpha1/kubernetes/zz_generated.deepcopy.go new file mode 100644 index 00000000..e021fbaa --- /dev/null +++ b/api/apps/v1alpha1/kubernetes/zz_generated.deepcopy.go @@ -0,0 +1,455 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package kubernetes + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APIServer) DeepCopyInto(out *APIServer) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIServer. +func (in *APIServer) DeepCopy() *APIServer { + if in == nil { + return nil + } + out := new(APIServer) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Addons) DeepCopyInto(out *Addons) { + *out = *in + in.CertManager.DeepCopyInto(&out.CertManager) + in.Cilium.DeepCopyInto(&out.Cilium) + in.Coredns.DeepCopyInto(&out.Coredns) + in.Fluxcd.DeepCopyInto(&out.Fluxcd) + out.GatewayAPI = in.GatewayAPI + in.GpuOperator.DeepCopyInto(&out.GpuOperator) + in.Hami.DeepCopyInto(&out.Hami) + in.IngressNginx.DeepCopyInto(&out.IngressNginx) + in.MonitoringAgents.DeepCopyInto(&out.MonitoringAgents) + in.Velero.DeepCopyInto(&out.Velero) + in.VerticalPodAutoscaler.DeepCopyInto(&out.VerticalPodAutoscaler) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Addons. +func (in *Addons) DeepCopy() *Addons { + if in == nil { + return nil + } + out := new(Addons) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CertManagerAddon) DeepCopyInto(out *CertManagerAddon) { + *out = *in + in.ValuesOverride.DeepCopyInto(&out.ValuesOverride) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertManagerAddon. +func (in *CertManagerAddon) DeepCopy() *CertManagerAddon { + if in == nil { + return nil + } + out := new(CertManagerAddon) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CiliumAddon) DeepCopyInto(out *CiliumAddon) { + *out = *in + in.ValuesOverride.DeepCopyInto(&out.ValuesOverride) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CiliumAddon. +func (in *CiliumAddon) DeepCopy() *CiliumAddon { + if in == nil { + return nil + } + out := new(CiliumAddon) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Config) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { + *out = *in + if in.NodeGroups != nil { + in, out := &in.NodeGroups, &out.NodeGroups + *out = make(map[string]NodeGroup, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + in.Addons.DeepCopyInto(&out.Addons) + in.ControlPlane.DeepCopyInto(&out.ControlPlane) + out.Images = in.Images +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. +func (in *ConfigSpec) DeepCopy() *ConfigSpec { + if in == nil { + return nil + } + out := new(ConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ControlPlane) DeepCopyInto(out *ControlPlane) { + *out = *in + in.ApiServer.DeepCopyInto(&out.ApiServer) + in.ControllerManager.DeepCopyInto(&out.ControllerManager) + in.Konnectivity.DeepCopyInto(&out.Konnectivity) + in.Scheduler.DeepCopyInto(&out.Scheduler) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControlPlane. +func (in *ControlPlane) DeepCopy() *ControlPlane { + if in == nil { + return nil + } + out := new(ControlPlane) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ControllerManager) DeepCopyInto(out *ControllerManager) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControllerManager. +func (in *ControllerManager) DeepCopy() *ControllerManager { + if in == nil { + return nil + } + out := new(ControllerManager) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CoreDNSAddon) DeepCopyInto(out *CoreDNSAddon) { + *out = *in + in.ValuesOverride.DeepCopyInto(&out.ValuesOverride) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CoreDNSAddon. +func (in *CoreDNSAddon) DeepCopy() *CoreDNSAddon { + if in == nil { + return nil + } + out := new(CoreDNSAddon) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FluxCDAddon) DeepCopyInto(out *FluxCDAddon) { + *out = *in + in.ValuesOverride.DeepCopyInto(&out.ValuesOverride) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FluxCDAddon. +func (in *FluxCDAddon) DeepCopy() *FluxCDAddon { + if in == nil { + return nil + } + out := new(FluxCDAddon) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GPU) DeepCopyInto(out *GPU) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GPU. +func (in *GPU) DeepCopy() *GPU { + if in == nil { + return nil + } + out := new(GPU) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GPUOperatorAddon) DeepCopyInto(out *GPUOperatorAddon) { + *out = *in + in.ValuesOverride.DeepCopyInto(&out.ValuesOverride) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GPUOperatorAddon. +func (in *GPUOperatorAddon) DeepCopy() *GPUOperatorAddon { + if in == nil { + return nil + } + out := new(GPUOperatorAddon) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GatewayAPIAddon) DeepCopyInto(out *GatewayAPIAddon) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayAPIAddon. +func (in *GatewayAPIAddon) DeepCopy() *GatewayAPIAddon { + if in == nil { + return nil + } + out := new(GatewayAPIAddon) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HAMiAddon) DeepCopyInto(out *HAMiAddon) { + *out = *in + in.ValuesOverride.DeepCopyInto(&out.ValuesOverride) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HAMiAddon. +func (in *HAMiAddon) DeepCopy() *HAMiAddon { + if in == nil { + return nil + } + out := new(HAMiAddon) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Images) DeepCopyInto(out *Images) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Images. +func (in *Images) DeepCopy() *Images { + if in == nil { + return nil + } + out := new(Images) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IngressNginxAddon) DeepCopyInto(out *IngressNginxAddon) { + *out = *in + if in.Hosts != nil { + in, out := &in.Hosts, &out.Hosts + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.ValuesOverride.DeepCopyInto(&out.ValuesOverride) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressNginxAddon. +func (in *IngressNginxAddon) DeepCopy() *IngressNginxAddon { + if in == nil { + return nil + } + out := new(IngressNginxAddon) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Konnectivity) DeepCopyInto(out *Konnectivity) { + *out = *in + in.Server.DeepCopyInto(&out.Server) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Konnectivity. +func (in *Konnectivity) DeepCopy() *Konnectivity { + if in == nil { + return nil + } + out := new(Konnectivity) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KonnectivityServer) DeepCopyInto(out *KonnectivityServer) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KonnectivityServer. +func (in *KonnectivityServer) DeepCopy() *KonnectivityServer { + if in == nil { + return nil + } + out := new(KonnectivityServer) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonitoringAgentsAddon) DeepCopyInto(out *MonitoringAgentsAddon) { + *out = *in + in.ValuesOverride.DeepCopyInto(&out.ValuesOverride) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonitoringAgentsAddon. +func (in *MonitoringAgentsAddon) DeepCopy() *MonitoringAgentsAddon { + if in == nil { + return nil + } + out := new(MonitoringAgentsAddon) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NodeGroup) DeepCopyInto(out *NodeGroup) { + *out = *in + out.EphemeralStorage = in.EphemeralStorage.DeepCopy() + if in.Gpus != nil { + in, out := &in.Gpus, &out.Gpus + *out = make([]GPU, len(*in)) + copy(*out, *in) + } + in.Resources.DeepCopyInto(&out.Resources) + if in.Roles != nil { + in, out := &in.Roles, &out.Roles + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeGroup. +func (in *NodeGroup) DeepCopy() *NodeGroup { + if in == nil { + return nil + } + out := new(NodeGroup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + out.Cpu = in.Cpu.DeepCopy() + out.Memory = in.Memory.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Scheduler) DeepCopyInto(out *Scheduler) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Scheduler. +func (in *Scheduler) DeepCopy() *Scheduler { + if in == nil { + return nil + } + out := new(Scheduler) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VeleroAddon) DeepCopyInto(out *VeleroAddon) { + *out = *in + in.ValuesOverride.DeepCopyInto(&out.ValuesOverride) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VeleroAddon. +func (in *VeleroAddon) DeepCopy() *VeleroAddon { + if in == nil { + return nil + } + out := new(VeleroAddon) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VerticalPodAutoscalerAddon) DeepCopyInto(out *VerticalPodAutoscalerAddon) { + *out = *in + in.ValuesOverride.DeepCopyInto(&out.ValuesOverride) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VerticalPodAutoscalerAddon. +func (in *VerticalPodAutoscalerAddon) DeepCopy() *VerticalPodAutoscalerAddon { + if in == nil { + return nil + } + out := new(VerticalPodAutoscalerAddon) + in.DeepCopyInto(out) + return out +} diff --git a/api/apps/v1alpha1/mariadb/types.go b/api/apps/v1alpha1/mariadb/types.go new file mode 100644 index 00000000..b3b6c414 --- /dev/null +++ b/api/apps/v1alpha1/mariadb/types.go @@ -0,0 +1,109 @@ +// Code generated by values-gen. DO NOT EDIT. +// +kubebuilder:object:generate=true +// +groupName=apps.cozystack.io +// +versionName=v1alpha1 +package mariadb + +import ( + resource "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +type Config struct { + v1.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + Spec ConfigSpec `json:"spec,omitempty"` +} + +type ConfigSpec struct { + // Number of MariaDB replicas. + // +kubebuilder:default:=2 + Replicas int `json:"replicas"` + // Explicit CPU and memory configuration for each MariaDB replica. When omitted, the preset defined in `resourcesPreset` is applied. + // +kubebuilder:default:={} + Resources Resources `json:"resources,omitempty"` + // Default sizing preset used when `resources` is omitted. + // +kubebuilder:default:="nano" + ResourcesPreset ResourcesPreset `json:"resourcesPreset"` + // Persistent Volume Claim size available for application data. + // +kubebuilder:default:="10Gi" + Size resource.Quantity `json:"size"` + // StorageClass used to store the data. + // +kubebuilder:default:="" + StorageClass string `json:"storageClass"` + // Enable external access from outside the cluster. + // +kubebuilder:default:=false + External bool `json:"external"` + // MariaDB major.minor version to deploy + // +kubebuilder:default:="v11.8" + Version Version `json:"version"` + // Users configuration map. + // +kubebuilder:default:={} + Users map[string]User `json:"users,omitempty"` + // Databases configuration map. + // +kubebuilder:default:={} + Databases map[string]Database `json:"databases,omitempty"` + // Backup configuration. + // +kubebuilder:default:={} + Backup Backup `json:"backup"` +} + +type Backup struct { + // Retention strategy for cleaning up old backups. + // +kubebuilder:default:="--keep-last=3 --keep-daily=3 --keep-within-weekly=1m" + CleanupStrategy string `json:"cleanupStrategy"` + // Enable regular backups (default: false). + // +kubebuilder:default:=false + Enabled bool `json:"enabled"` + // Password for Restic backup encryption. + // +kubebuilder:default:="" + ResticPassword string `json:"resticPassword"` + // Access key for S3 authentication. + // +kubebuilder:default:="" + S3AccessKey string `json:"s3AccessKey"` + // S3 bucket used for storing backups. + // +kubebuilder:default:="s3.example.org/mariadb-backups" + S3Bucket string `json:"s3Bucket"` + // AWS S3 region where backups are stored. + // +kubebuilder:default:="us-east-1" + S3Region string `json:"s3Region"` + // Secret key for S3 authentication. + // +kubebuilder:default:="" + S3SecretKey string `json:"s3SecretKey"` + // Cron schedule for automated backups. + // +kubebuilder:default:="0 2 * * *" + Schedule string `json:"schedule"` +} + +type Database struct { + // Roles assigned to users. + Roles DatabaseRoles `json:"roles,omitempty"` +} + +type DatabaseRoles struct { + // List of users with admin privileges. + Admin []string `json:"admin,omitempty"` + // List of users with read-only privileges. + Readonly []string `json:"readonly,omitempty"` +} + +type Resources struct { + // CPU available to each replica. + Cpu resource.Quantity `json:"cpu,omitempty"` + // Memory (RAM) available to each replica. + Memory resource.Quantity `json:"memory,omitempty"` +} + +type User struct { + // Maximum number of connections. + MaxUserConnections int `json:"maxUserConnections"` + // Password for the user. + Password string `json:"password"` +} + +// +kubebuilder:validation:Enum="nano";"micro";"small";"medium";"large";"xlarge";"2xlarge" +type ResourcesPreset string + +// +kubebuilder:validation:Enum="v11.8";"v11.4";"v10.11";"v10.6" +type Version string diff --git a/api/apps/v1alpha1/mariadb/zz_generated.deepcopy.go b/api/apps/v1alpha1/mariadb/zz_generated.deepcopy.go new file mode 100644 index 00000000..4cc5a94f --- /dev/null +++ b/api/apps/v1alpha1/mariadb/zz_generated.deepcopy.go @@ -0,0 +1,171 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package mariadb + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Backup) DeepCopyInto(out *Backup) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Backup. +func (in *Backup) DeepCopy() *Backup { + if in == nil { + return nil + } + out := new(Backup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Config) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) + out.Size = in.Size.DeepCopy() + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make(map[string]User, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Databases != nil { + in, out := &in.Databases, &out.Databases + *out = make(map[string]Database, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + out.Backup = in.Backup +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. +func (in *ConfigSpec) DeepCopy() *ConfigSpec { + if in == nil { + return nil + } + out := new(ConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Database) DeepCopyInto(out *Database) { + *out = *in + in.Roles.DeepCopyInto(&out.Roles) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Database. +func (in *Database) DeepCopy() *Database { + if in == nil { + return nil + } + out := new(Database) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseRoles) DeepCopyInto(out *DatabaseRoles) { + *out = *in + if in.Admin != nil { + in, out := &in.Admin, &out.Admin + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Readonly != nil { + in, out := &in.Readonly, &out.Readonly + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseRoles. +func (in *DatabaseRoles) DeepCopy() *DatabaseRoles { + if in == nil { + return nil + } + out := new(DatabaseRoles) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + out.Cpu = in.Cpu.DeepCopy() + out.Memory = in.Memory.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *User) DeepCopyInto(out *User) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new User. +func (in *User) DeepCopy() *User { + if in == nil { + return nil + } + out := new(User) + in.DeepCopyInto(out) + return out +} diff --git a/api/apps/v1alpha1/mongodb/types.go b/api/apps/v1alpha1/mongodb/types.go new file mode 100644 index 00000000..886b716c --- /dev/null +++ b/api/apps/v1alpha1/mongodb/types.go @@ -0,0 +1,149 @@ +// Code generated by values-gen. DO NOT EDIT. +// +kubebuilder:object:generate=true +// +groupName=apps.cozystack.io +// +versionName=v1alpha1 +package mongodb + +import ( + resource "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +type Config struct { + v1.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + Spec ConfigSpec `json:"spec,omitempty"` +} + +type ConfigSpec struct { + // Number of MongoDB replicas in replica set. + // +kubebuilder:default:=3 + Replicas int `json:"replicas"` + // Explicit CPU and memory configuration for each MongoDB replica. When omitted, the preset defined in `resourcesPreset` is applied. + // +kubebuilder:default:={} + Resources Resources `json:"resources,omitempty"` + // Default sizing preset used when `resources` is omitted. + // +kubebuilder:default:="small" + ResourcesPreset ResourcesPreset `json:"resourcesPreset"` + // Persistent Volume Claim size available for application data. + // +kubebuilder:default:="10Gi" + Size resource.Quantity `json:"size"` + // StorageClass used to store the data. + // +kubebuilder:default:="" + StorageClass string `json:"storageClass"` + // Enable external access from outside the cluster. + // +kubebuilder:default:=false + External bool `json:"external"` + // MongoDB major version to deploy. + // +kubebuilder:default:="v8" + Version Version `json:"version"` + // Enable sharded cluster mode. When disabled, deploys a replica set. + // +kubebuilder:default:=false + Sharding bool `json:"sharding"` + // Configuration for sharded cluster mode. + // +kubebuilder:default:={} + ShardingConfig ShardingConfig `json:"shardingConfig"` + // Users configuration map. + // +kubebuilder:default:={} + Users map[string]User `json:"users,omitempty"` + // Databases configuration map. + // +kubebuilder:default:={} + Databases map[string]Database `json:"databases,omitempty"` + // Backup configuration. + // +kubebuilder:default:={} + Backup Backup `json:"backup"` + // Bootstrap configuration. + // +kubebuilder:default:={} + Bootstrap Bootstrap `json:"bootstrap"` +} + +type Backup struct { + // Destination path for backups (e.g. s3://bucket/path/). + // +kubebuilder:default:="s3://bucket/path/to/folder/" + DestinationPath string `json:"destinationPath,omitempty"` + // Enable regular backups. + // +kubebuilder:default:=false + Enabled bool `json:"enabled"` + // S3 endpoint URL for uploads. + // +kubebuilder:default:="http://minio-gateway-service:9000" + EndpointURL string `json:"endpointURL,omitempty"` + // Retention policy (e.g. "30d"). + // +kubebuilder:default:="30d" + RetentionPolicy string `json:"retentionPolicy,omitempty"` + // Access key for S3 authentication. + // +kubebuilder:default:="" + S3AccessKey string `json:"s3AccessKey,omitempty"` + // Secret key for S3 authentication. + // +kubebuilder:default:="" + S3SecretKey string `json:"s3SecretKey,omitempty"` + // Cron schedule for automated backups. + // +kubebuilder:default:="0 2 * * *" + Schedule string `json:"schedule,omitempty"` +} + +type Bootstrap struct { + // Name of backup to restore from. + // +kubebuilder:default:="" + BackupName string `json:"backupName"` + // Whether to restore from a backup. + // +kubebuilder:default:=false + Enabled bool `json:"enabled"` + // Timestamp for point-in-time recovery; empty means latest. + // +kubebuilder:default:="" + RecoveryTime string `json:"recoveryTime,omitempty"` +} + +type Database struct { + // Roles assigned to users. + Roles DatabaseRoles `json:"roles,omitempty"` +} + +type DatabaseRoles struct { + // List of users with admin privileges (readWrite + dbAdmin). + Admin []string `json:"admin,omitempty"` + // List of users with read-only privileges. + Readonly []string `json:"readonly,omitempty"` +} + +type Resources struct { + // CPU available to each replica. + Cpu resource.Quantity `json:"cpu,omitempty"` + // Memory (RAM) available to each replica. + Memory resource.Quantity `json:"memory,omitempty"` +} + +type Shard struct { + // Shard name. + Name string `json:"name"` + // Number of replicas in this shard. + Replicas int `json:"replicas"` + // PVC size for this shard. + Size resource.Quantity `json:"size"` +} + +type ShardingConfig struct { + // PVC size for config servers. + // +kubebuilder:default:="3Gi" + ConfigServerSize resource.Quantity `json:"configServerSize"` + // Number of config server replicas. + // +kubebuilder:default:=3 + ConfigServers int `json:"configServers"` + // Number of mongos router replicas. + // +kubebuilder:default:=2 + Mongos int `json:"mongos"` + // List of shard configurations. + // +kubebuilder:default:={{"name":"rs0","replicas":3,"size":"10Gi"}} + Shards []Shard `json:"shards,omitempty"` +} + +type User struct { + // Password for the user (auto-generated if omitted). + Password string `json:"password,omitempty"` +} + +// +kubebuilder:validation:Enum="nano";"micro";"small";"medium";"large";"xlarge";"2xlarge" +type ResourcesPreset string + +// +kubebuilder:validation:Enum="v8";"v7";"v6" +type Version string diff --git a/api/apps/v1alpha1/mongodb/zz_generated.deepcopy.go b/api/apps/v1alpha1/mongodb/zz_generated.deepcopy.go new file mode 100644 index 00000000..694009f4 --- /dev/null +++ b/api/apps/v1alpha1/mongodb/zz_generated.deepcopy.go @@ -0,0 +1,227 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package mongodb + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Backup) DeepCopyInto(out *Backup) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Backup. +func (in *Backup) DeepCopy() *Backup { + if in == nil { + return nil + } + out := new(Backup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Bootstrap) DeepCopyInto(out *Bootstrap) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Bootstrap. +func (in *Bootstrap) DeepCopy() *Bootstrap { + if in == nil { + return nil + } + out := new(Bootstrap) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Config) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) + out.Size = in.Size.DeepCopy() + in.ShardingConfig.DeepCopyInto(&out.ShardingConfig) + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make(map[string]User, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Databases != nil { + in, out := &in.Databases, &out.Databases + *out = make(map[string]Database, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + out.Backup = in.Backup + out.Bootstrap = in.Bootstrap +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. +func (in *ConfigSpec) DeepCopy() *ConfigSpec { + if in == nil { + return nil + } + out := new(ConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Database) DeepCopyInto(out *Database) { + *out = *in + in.Roles.DeepCopyInto(&out.Roles) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Database. +func (in *Database) DeepCopy() *Database { + if in == nil { + return nil + } + out := new(Database) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseRoles) DeepCopyInto(out *DatabaseRoles) { + *out = *in + if in.Admin != nil { + in, out := &in.Admin, &out.Admin + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Readonly != nil { + in, out := &in.Readonly, &out.Readonly + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseRoles. +func (in *DatabaseRoles) DeepCopy() *DatabaseRoles { + if in == nil { + return nil + } + out := new(DatabaseRoles) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + out.Cpu = in.Cpu.DeepCopy() + out.Memory = in.Memory.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Shard) DeepCopyInto(out *Shard) { + *out = *in + out.Size = in.Size.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Shard. +func (in *Shard) DeepCopy() *Shard { + if in == nil { + return nil + } + out := new(Shard) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ShardingConfig) DeepCopyInto(out *ShardingConfig) { + *out = *in + out.ConfigServerSize = in.ConfigServerSize.DeepCopy() + if in.Shards != nil { + in, out := &in.Shards, &out.Shards + *out = make([]Shard, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShardingConfig. +func (in *ShardingConfig) DeepCopy() *ShardingConfig { + if in == nil { + return nil + } + out := new(ShardingConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *User) DeepCopyInto(out *User) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new User. +func (in *User) DeepCopy() *User { + if in == nil { + return nil + } + out := new(User) + in.DeepCopyInto(out) + return out +} diff --git a/api/apps/v1alpha1/nats/types.go b/api/apps/v1alpha1/nats/types.go new file mode 100644 index 00000000..5437355d --- /dev/null +++ b/api/apps/v1alpha1/nats/types.go @@ -0,0 +1,78 @@ +// Code generated by values-gen. DO NOT EDIT. +// +kubebuilder:object:generate=true +// +groupName=apps.cozystack.io +// +versionName=v1alpha1 +package nats + +import ( + resource "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/apis/meta/v1" + k8sRuntime "k8s.io/apimachinery/pkg/runtime" +) + +// +kubebuilder:object:root=true +type Config struct { + v1.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + Spec ConfigSpec `json:"spec,omitempty"` +} + +type ConfigSpec struct { + // Number of replicas. + // +kubebuilder:default:=2 + Replicas int `json:"replicas"` + // Explicit CPU and memory configuration for each NATS replica. When omitted, the preset defined in `resourcesPreset` is applied. + // +kubebuilder:default:={} + Resources Resources `json:"resources,omitempty"` + // Default sizing preset used when `resources` is omitted. + // +kubebuilder:default:="nano" + ResourcesPreset ResourcesPreset `json:"resourcesPreset"` + // StorageClass used to store the data. + // +kubebuilder:default:="" + StorageClass string `json:"storageClass"` + // Enable external access from outside the cluster. + // +kubebuilder:default:=false + External bool `json:"external"` + // Users configuration map. + // +kubebuilder:default:={} + Users map[string]User `json:"users,omitempty"` + // Jetstream configuration. + // +kubebuilder:default:={} + Jetstream Jetstream `json:"jetstream"` + // NATS configuration. + // +kubebuilder:default:={} + Config ValuesConfig `json:"config"` +} + +type ValuesConfig struct { + // Additional configuration to merge into NATS config. + // +kubebuilder:default:={} + Merge *k8sRuntime.RawExtension `json:"merge,omitempty"` + // Additional resolver configuration to merge into NATS config. + // +kubebuilder:default:={} + Resolver *k8sRuntime.RawExtension `json:"resolver,omitempty"` +} + +type Jetstream struct { + // Enable or disable Jetstream for persistent messaging in NATS. + // +kubebuilder:default:=true + Enabled bool `json:"enabled"` + // Jetstream persistent storage size. + // +kubebuilder:default:="10Gi" + Size resource.Quantity `json:"size"` +} + +type Resources struct { + // CPU available to each replica. + Cpu resource.Quantity `json:"cpu,omitempty"` + // Memory (RAM) available to each replica. + Memory resource.Quantity `json:"memory,omitempty"` +} + +type User struct { + // Password for the user. + Password string `json:"password,omitempty"` +} + +// +kubebuilder:validation:Enum="nano";"micro";"small";"medium";"large";"xlarge";"2xlarge" +type ResourcesPreset string diff --git a/api/apps/v1alpha1/nats/zz_generated.deepcopy.go b/api/apps/v1alpha1/nats/zz_generated.deepcopy.go new file mode 100644 index 00000000..f6c23cb1 --- /dev/null +++ b/api/apps/v1alpha1/nats/zz_generated.deepcopy.go @@ -0,0 +1,149 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package nats + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Config) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make(map[string]User, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.Jetstream.DeepCopyInto(&out.Jetstream) + in.Config.DeepCopyInto(&out.Config) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. +func (in *ConfigSpec) DeepCopy() *ConfigSpec { + if in == nil { + return nil + } + out := new(ConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Jetstream) DeepCopyInto(out *Jetstream) { + *out = *in + out.Size = in.Size.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Jetstream. +func (in *Jetstream) DeepCopy() *Jetstream { + if in == nil { + return nil + } + out := new(Jetstream) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + out.Cpu = in.Cpu.DeepCopy() + out.Memory = in.Memory.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *User) DeepCopyInto(out *User) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new User. +func (in *User) DeepCopy() *User { + if in == nil { + return nil + } + out := new(User) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ValuesConfig) DeepCopyInto(out *ValuesConfig) { + *out = *in + if in.Merge != nil { + in, out := &in.Merge, &out.Merge + *out = new(runtime.RawExtension) + (*in).DeepCopyInto(*out) + } + if in.Resolver != nil { + in, out := &in.Resolver, &out.Resolver + *out = new(runtime.RawExtension) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ValuesConfig. +func (in *ValuesConfig) DeepCopy() *ValuesConfig { + if in == nil { + return nil + } + out := new(ValuesConfig) + in.DeepCopyInto(out) + return out +} diff --git a/api/apps/v1alpha1/openbao/types.go b/api/apps/v1alpha1/openbao/types.go new file mode 100644 index 00000000..bb5d12d9 --- /dev/null +++ b/api/apps/v1alpha1/openbao/types.go @@ -0,0 +1,51 @@ +// Code generated by values-gen. DO NOT EDIT. +// +kubebuilder:object:generate=true +// +groupName=apps.cozystack.io +// +versionName=v1alpha1 +package openbao + +import ( + resource "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +type Config struct { + v1.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + Spec ConfigSpec `json:"spec,omitempty"` +} + +type ConfigSpec struct { + // Number of OpenBAO replicas. HA with Raft is automatically enabled when replicas > 1. Switching between standalone (file storage) and HA (Raft storage) modes requires data migration. + // +kubebuilder:default:=1 + Replicas int `json:"replicas"` + // Explicit CPU and memory configuration for each OpenBAO replica. When omitted, the preset defined in `resourcesPreset` is applied. + // +kubebuilder:default:={} + Resources Resources `json:"resources,omitempty"` + // Default sizing preset used when `resources` is omitted. + // +kubebuilder:default:="small" + ResourcesPreset ResourcesPreset `json:"resourcesPreset"` + // Persistent Volume Claim size for data storage. + // +kubebuilder:default:="10Gi" + Size resource.Quantity `json:"size"` + // StorageClass used to store the data. + // +kubebuilder:default:="" + StorageClass string `json:"storageClass"` + // Enable external access from outside the cluster. + // +kubebuilder:default:=false + External bool `json:"external"` + // Enable the OpenBAO web UI. + // +kubebuilder:default:=true + Ui bool `json:"ui"` +} + +type Resources struct { + // CPU available to each replica. + Cpu resource.Quantity `json:"cpu,omitempty"` + // Memory (RAM) available to each replica. + Memory resource.Quantity `json:"memory,omitempty"` +} + +// +kubebuilder:validation:Enum="nano";"micro";"small";"medium";"large";"xlarge";"2xlarge" +type ResourcesPreset string diff --git a/api/apps/v1alpha1/openbao/zz_generated.deepcopy.go b/api/apps/v1alpha1/openbao/zz_generated.deepcopy.go new file mode 100644 index 00000000..baa1258b --- /dev/null +++ b/api/apps/v1alpha1/openbao/zz_generated.deepcopy.go @@ -0,0 +1,85 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package openbao + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Config) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) + out.Size = in.Size.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. +func (in *ConfigSpec) DeepCopy() *ConfigSpec { + if in == nil { + return nil + } + out := new(ConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + out.Cpu = in.Cpu.DeepCopy() + out.Memory = in.Memory.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} diff --git a/api/apps/v1alpha1/opensearch/types.go b/api/apps/v1alpha1/opensearch/types.go new file mode 100644 index 00000000..b188cdfa --- /dev/null +++ b/api/apps/v1alpha1/opensearch/types.go @@ -0,0 +1,115 @@ +// Code generated by values-gen. DO NOT EDIT. +// +kubebuilder:object:generate=true +// +groupName=apps.cozystack.io +// +versionName=v1alpha1 +package opensearch + +import ( + resource "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +type Config struct { + v1.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + Spec ConfigSpec `json:"spec,omitempty"` +} + +type ConfigSpec struct { + // Number of OpenSearch nodes in the cluster. + // +kubebuilder:default:=3 + Replicas int `json:"replicas"` + // Explicit CPU and memory configuration for each OpenSearch node. When omitted, the preset defined in `resourcesPreset` is applied. + // +kubebuilder:default:={} + Resources Resources `json:"resources,omitempty"` + // Default sizing preset used when `resources` is omitted. OpenSearch requires minimum 2Gi memory. + // +kubebuilder:default:="large" + ResourcesPreset ResourcesPreset `json:"resourcesPreset"` + // Persistent Volume Claim size available for application data. + // +kubebuilder:default:="10Gi" + Size resource.Quantity `json:"size"` + // StorageClass used to store the data. + // +kubebuilder:default:="" + StorageClass string `json:"storageClass"` + // Enable external access from outside the cluster. + // +kubebuilder:default:=false + External bool `json:"external"` + // How strictly to enforce pod distribution across nodes and zones. + // +kubebuilder:default:="soft" + TopologySpreadPolicy TopologySpreadPolicy `json:"topologySpreadPolicy"` + // OpenSearch major version to deploy. + // +kubebuilder:default:="v2" + Version Version `json:"version"` + // Container images used by the operator. + // +kubebuilder:default:={} + Images Images `json:"images"` + // Node roles configuration. + // +kubebuilder:default:={} + NodeRoles NodeRoles `json:"nodeRoles"` + // Custom OpenSearch users configuration map. + // +kubebuilder:default:={} + Users map[string]User `json:"users,omitempty"` + // OpenSearch Dashboards configuration. + // +kubebuilder:default:={} + Dashboards Dashboards `json:"dashboards"` +} + +type Dashboards struct { + // Enable OpenSearch Dashboards deployment. + // +kubebuilder:default:=false + Enabled bool `json:"enabled"` + // Number of Dashboards replicas. + // +kubebuilder:default:=1 + Replicas int `json:"replicas"` + // Explicit CPU and memory configuration for Dashboards. + // +kubebuilder:default:={} + Resources Resources `json:"resources,omitempty"` + // Default sizing preset for Dashboards. + // +kubebuilder:default:="medium" + ResourcesPreset ResourcesPreset `json:"resourcesPreset"` +} + +type Images struct { + // OpenSearch image. + // +kubebuilder:default:="" + Opensearch string `json:"opensearch"` +} + +type NodeRoles struct { + // Enable data role. + // +kubebuilder:default:=true + Data bool `json:"data"` + // Enable ingest role. + // +kubebuilder:default:=true + Ingest bool `json:"ingest"` + // Enable cluster_manager role. + // +kubebuilder:default:=true + Master bool `json:"master"` + // Enable machine learning role. + // +kubebuilder:default:=false + Ml bool `json:"ml"` +} + +type Resources struct { + // CPU available to each node. + Cpu resource.Quantity `json:"cpu,omitempty"` + // Memory (RAM) available to each node. + Memory resource.Quantity `json:"memory,omitempty"` +} + +type User struct { + // Password for the user (auto-generated if omitted). + Password string `json:"password,omitempty"` + // List of OpenSearch roles. + Roles []string `json:"roles,omitempty"` +} + +// +kubebuilder:validation:Enum="nano";"micro";"small";"medium";"large";"xlarge";"2xlarge" +type ResourcesPreset string + +// +kubebuilder:validation:Enum="soft";"hard" +type TopologySpreadPolicy string + +// +kubebuilder:validation:Enum="v3";"v2";"v1" +type Version string diff --git a/api/apps/v1alpha1/opensearch/zz_generated.deepcopy.go b/api/apps/v1alpha1/opensearch/zz_generated.deepcopy.go new file mode 100644 index 00000000..f7f2daef --- /dev/null +++ b/api/apps/v1alpha1/opensearch/zz_generated.deepcopy.go @@ -0,0 +1,161 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package opensearch + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Config) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) + out.Size = in.Size.DeepCopy() + out.Images = in.Images + out.NodeRoles = in.NodeRoles + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make(map[string]User, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + in.Dashboards.DeepCopyInto(&out.Dashboards) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. +func (in *ConfigSpec) DeepCopy() *ConfigSpec { + if in == nil { + return nil + } + out := new(ConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Dashboards) DeepCopyInto(out *Dashboards) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Dashboards. +func (in *Dashboards) DeepCopy() *Dashboards { + if in == nil { + return nil + } + out := new(Dashboards) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Images) DeepCopyInto(out *Images) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Images. +func (in *Images) DeepCopy() *Images { + if in == nil { + return nil + } + out := new(Images) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NodeRoles) DeepCopyInto(out *NodeRoles) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeRoles. +func (in *NodeRoles) DeepCopy() *NodeRoles { + if in == nil { + return nil + } + out := new(NodeRoles) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + out.Cpu = in.Cpu.DeepCopy() + out.Memory = in.Memory.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *User) DeepCopyInto(out *User) { + *out = *in + if in.Roles != nil { + in, out := &in.Roles, &out.Roles + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new User. +func (in *User) DeepCopy() *User { + if in == nil { + return nil + } + out := new(User) + in.DeepCopyInto(out) + return out +} diff --git a/api/apps/v1alpha1/postgresql/types.go b/api/apps/v1alpha1/postgresql/types.go new file mode 100644 index 00000000..a2ff77a8 --- /dev/null +++ b/api/apps/v1alpha1/postgresql/types.go @@ -0,0 +1,153 @@ +// Code generated by values-gen. DO NOT EDIT. +// +kubebuilder:object:generate=true +// +groupName=apps.cozystack.io +// +versionName=v1alpha1 +package postgresql + +import ( + resource "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +type Config struct { + v1.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + Spec ConfigSpec `json:"spec,omitempty"` +} + +type ConfigSpec struct { + // Number of Postgres replicas. + // +kubebuilder:default:=2 + Replicas int `json:"replicas"` + // Explicit CPU and memory configuration for each PostgreSQL replica. When omitted, the preset defined in `resourcesPreset` is applied. + // +kubebuilder:default:={} + Resources Resources `json:"resources,omitempty"` + // Default sizing preset used when `resources` is omitted. + // +kubebuilder:default:="micro" + ResourcesPreset ResourcesPreset `json:"resourcesPreset"` + // Persistent Volume Claim size available for application data. + // +kubebuilder:default:="10Gi" + Size resource.Quantity `json:"size"` + // StorageClass used to store the data. + // +kubebuilder:default:="" + StorageClass string `json:"storageClass"` + // Enable external access from outside the cluster. + // +kubebuilder:default:=false + External bool `json:"external"` + // PostgreSQL major version to deploy + // +kubebuilder:default:="v18" + Version Version `json:"version"` + // PostgreSQL server configuration. + // +kubebuilder:default:={} + Postgresql PostgreSQL `json:"postgresql"` + // Quorum configuration for synchronous replication. + // +kubebuilder:default:={} + Quorum Quorum `json:"quorum"` + // Users configuration map. + // +kubebuilder:default:={} + Users map[string]User `json:"users,omitempty"` + // Databases configuration map. + // +kubebuilder:default:={} + Databases map[string]Database `json:"databases,omitempty"` + // Backup configuration. + // +kubebuilder:default:={} + Backup Backup `json:"backup"` + // Bootstrap configuration. + // +kubebuilder:default:={} + Bootstrap Bootstrap `json:"bootstrap"` +} + +type Backup struct { + // Destination path for backups (e.g. s3://bucket/path/). + // +kubebuilder:default:="s3://bucket/path/to/folder/" + DestinationPath string `json:"destinationPath,omitempty"` + // Enable regular backups. + // +kubebuilder:default:=false + Enabled bool `json:"enabled"` + // S3 endpoint URL for uploads. + // +kubebuilder:default:="http://minio-gateway-service:9000" + EndpointURL string `json:"endpointURL,omitempty"` + // Retention policy (e.g. "30d"). + // +kubebuilder:default:="30d" + RetentionPolicy string `json:"retentionPolicy,omitempty"` + // Access key for S3 authentication. + // +kubebuilder:default:="" + S3AccessKey string `json:"s3AccessKey,omitempty"` + // Secret key for S3 authentication. + // +kubebuilder:default:="" + S3SecretKey string `json:"s3SecretKey,omitempty"` + // Cron schedule for automated backups. + // +kubebuilder:default:="0 2 * * * *" + Schedule string `json:"schedule,omitempty"` +} + +type Bootstrap struct { + // Whether to restore from a backup. + // +kubebuilder:default:=false + Enabled bool `json:"enabled"` + // Previous cluster name before deletion. + // +kubebuilder:default:="" + OldName string `json:"oldName"` + // Timestamp (RFC3339) for point-in-time recovery; empty means latest. + // +kubebuilder:default:="" + RecoveryTime string `json:"recoveryTime,omitempty"` + // Barman server name (S3 path prefix) used by the original cluster when writing backups. Set this only when the original cluster had an explicit barmanObjectStore.serverName that differed from its Kubernetes resource name. + // +kubebuilder:default:="" + ServerName string `json:"serverName,omitempty"` +} + +type Database struct { + // List of enabled PostgreSQL extensions. + Extensions []string `json:"extensions,omitempty"` + // Roles assigned to users. + Roles DatabaseRoles `json:"roles,omitempty"` +} + +type DatabaseRoles struct { + // List of users with admin privileges. + Admin []string `json:"admin,omitempty"` + // List of users with read-only privileges. + Readonly []string `json:"readonly,omitempty"` +} + +type PostgreSQL struct { + // PostgreSQL server parameters. + // +kubebuilder:default:={} + Parameters PostgreSQLParameters `json:"parameters,omitempty"` +} + +type PostgreSQLParameters struct { + // Maximum number of concurrent connections to the database server. + // +kubebuilder:default:=100 + MaxConnections int `json:"max_connections,omitempty"` +} + +type Quorum struct { + // Maximum number of synchronous replicas allowed (must be less than total replicas). + // +kubebuilder:default:=0 + MaxSyncReplicas int `json:"maxSyncReplicas"` + // Minimum number of synchronous replicas required for commit. + // +kubebuilder:default:=0 + MinSyncReplicas int `json:"minSyncReplicas"` +} + +type Resources struct { + // CPU available to each replica. + Cpu resource.Quantity `json:"cpu,omitempty"` + // Memory (RAM) available to each replica. + Memory resource.Quantity `json:"memory,omitempty"` +} + +type User struct { + // Password for the user. + Password string `json:"password,omitempty"` + // Whether the user has replication privileges. + Replication bool `json:"replication,omitempty"` +} + +// +kubebuilder:validation:Enum="nano";"micro";"small";"medium";"large";"xlarge";"2xlarge" +type ResourcesPreset string + +// +kubebuilder:validation:Enum="v18";"v17";"v16";"v15";"v14";"v13" +type Version string diff --git a/api/apps/v1alpha1/postgresql/zz_generated.deepcopy.go b/api/apps/v1alpha1/postgresql/zz_generated.deepcopy.go new file mode 100644 index 00000000..5fba855e --- /dev/null +++ b/api/apps/v1alpha1/postgresql/zz_generated.deepcopy.go @@ -0,0 +1,240 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package postgresql + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Backup) DeepCopyInto(out *Backup) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Backup. +func (in *Backup) DeepCopy() *Backup { + if in == nil { + return nil + } + out := new(Backup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Bootstrap) DeepCopyInto(out *Bootstrap) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Bootstrap. +func (in *Bootstrap) DeepCopy() *Bootstrap { + if in == nil { + return nil + } + out := new(Bootstrap) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Config) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) + out.Size = in.Size.DeepCopy() + out.Postgresql = in.Postgresql + out.Quorum = in.Quorum + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make(map[string]User, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Databases != nil { + in, out := &in.Databases, &out.Databases + *out = make(map[string]Database, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + out.Backup = in.Backup + out.Bootstrap = in.Bootstrap +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. +func (in *ConfigSpec) DeepCopy() *ConfigSpec { + if in == nil { + return nil + } + out := new(ConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Database) DeepCopyInto(out *Database) { + *out = *in + if in.Extensions != nil { + in, out := &in.Extensions, &out.Extensions + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.Roles.DeepCopyInto(&out.Roles) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Database. +func (in *Database) DeepCopy() *Database { + if in == nil { + return nil + } + out := new(Database) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseRoles) DeepCopyInto(out *DatabaseRoles) { + *out = *in + if in.Admin != nil { + in, out := &in.Admin, &out.Admin + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Readonly != nil { + in, out := &in.Readonly, &out.Readonly + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseRoles. +func (in *DatabaseRoles) DeepCopy() *DatabaseRoles { + if in == nil { + return nil + } + out := new(DatabaseRoles) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgreSQL) DeepCopyInto(out *PostgreSQL) { + *out = *in + out.Parameters = in.Parameters +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgreSQL. +func (in *PostgreSQL) DeepCopy() *PostgreSQL { + if in == nil { + return nil + } + out := new(PostgreSQL) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgreSQLParameters) DeepCopyInto(out *PostgreSQLParameters) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgreSQLParameters. +func (in *PostgreSQLParameters) DeepCopy() *PostgreSQLParameters { + if in == nil { + return nil + } + out := new(PostgreSQLParameters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Quorum) DeepCopyInto(out *Quorum) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Quorum. +func (in *Quorum) DeepCopy() *Quorum { + if in == nil { + return nil + } + out := new(Quorum) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + out.Cpu = in.Cpu.DeepCopy() + out.Memory = in.Memory.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *User) DeepCopyInto(out *User) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new User. +func (in *User) DeepCopy() *User { + if in == nil { + return nil + } + out := new(User) + in.DeepCopyInto(out) + return out +} diff --git a/api/apps/v1alpha1/qdrant/types.go b/api/apps/v1alpha1/qdrant/types.go new file mode 100644 index 00000000..203e9a97 --- /dev/null +++ b/api/apps/v1alpha1/qdrant/types.go @@ -0,0 +1,48 @@ +// Code generated by values-gen. DO NOT EDIT. +// +kubebuilder:object:generate=true +// +groupName=apps.cozystack.io +// +versionName=v1alpha1 +package qdrant + +import ( + resource "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +type Config struct { + v1.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + Spec ConfigSpec `json:"spec,omitempty"` +} + +type ConfigSpec struct { + // Number of Qdrant replicas. Cluster mode is automatically enabled when replicas > 1. + // +kubebuilder:default:=1 + Replicas int `json:"replicas"` + // Explicit CPU and memory configuration for each Qdrant replica. When omitted, the preset defined in `resourcesPreset` is applied. + // +kubebuilder:default:={} + Resources Resources `json:"resources,omitempty"` + // Default sizing preset used when `resources` is omitted. + // +kubebuilder:default:="small" + ResourcesPreset ResourcesPreset `json:"resourcesPreset"` + // Persistent Volume Claim size available for vector data storage. + // +kubebuilder:default:="10Gi" + Size resource.Quantity `json:"size"` + // StorageClass used to store the data. + // +kubebuilder:default:="" + StorageClass string `json:"storageClass"` + // Enable external access from outside the cluster. + // +kubebuilder:default:=false + External bool `json:"external"` +} + +type Resources struct { + // CPU available to each replica. + Cpu resource.Quantity `json:"cpu,omitempty"` + // Memory (RAM) available to each replica. + Memory resource.Quantity `json:"memory,omitempty"` +} + +// +kubebuilder:validation:Enum="nano";"micro";"small";"medium";"large";"xlarge";"2xlarge" +type ResourcesPreset string diff --git a/api/apps/v1alpha1/qdrant/zz_generated.deepcopy.go b/api/apps/v1alpha1/qdrant/zz_generated.deepcopy.go new file mode 100644 index 00000000..2abfd37e --- /dev/null +++ b/api/apps/v1alpha1/qdrant/zz_generated.deepcopy.go @@ -0,0 +1,85 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package qdrant + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Config) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) + out.Size = in.Size.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. +func (in *ConfigSpec) DeepCopy() *ConfigSpec { + if in == nil { + return nil + } + out := new(ConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + out.Cpu = in.Cpu.DeepCopy() + out.Memory = in.Memory.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} diff --git a/api/apps/v1alpha1/rabbitmq/types.go b/api/apps/v1alpha1/rabbitmq/types.go new file mode 100644 index 00000000..f67c4402 --- /dev/null +++ b/api/apps/v1alpha1/rabbitmq/types.go @@ -0,0 +1,77 @@ +// Code generated by values-gen. DO NOT EDIT. +// +kubebuilder:object:generate=true +// +groupName=apps.cozystack.io +// +versionName=v1alpha1 +package rabbitmq + +import ( + resource "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +type Config struct { + v1.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + Spec ConfigSpec `json:"spec,omitempty"` +} + +type ConfigSpec struct { + // Number of RabbitMQ replicas. + // +kubebuilder:default:=3 + Replicas int `json:"replicas"` + // Explicit CPU and memory configuration for each RabbitMQ replica. When omitted, the preset defined in `resourcesPreset` is applied. + // +kubebuilder:default:={} + Resources Resources `json:"resources,omitempty"` + // Default sizing preset used when `resources` is omitted. + // +kubebuilder:default:="nano" + ResourcesPreset ResourcesPreset `json:"resourcesPreset"` + // Persistent Volume Claim size available for application data. + // +kubebuilder:default:="10Gi" + Size resource.Quantity `json:"size"` + // StorageClass used to store the data. + // +kubebuilder:default:="" + StorageClass string `json:"storageClass"` + // Enable external access from outside the cluster. + // +kubebuilder:default:=false + External bool `json:"external"` + // RabbitMQ major.minor version to deploy + // +kubebuilder:default:="v4.2" + Version Version `json:"version"` + // Users configuration map. + // +kubebuilder:default:={} + Users map[string]User `json:"users,omitempty"` + // Virtual hosts configuration map. + // +kubebuilder:default:={} + Vhosts map[string]Vhost `json:"vhosts,omitempty"` +} + +type Resources struct { + // CPU available to each replica. + Cpu resource.Quantity `json:"cpu,omitempty"` + // Memory (RAM) available to each replica. + Memory resource.Quantity `json:"memory,omitempty"` +} + +type Roles struct { + // List of admin users. + Admin []string `json:"admin,omitempty"` + // List of readonly users. + Readonly []string `json:"readonly,omitempty"` +} + +type User struct { + // Password for the user. + Password string `json:"password,omitempty"` +} + +type Vhost struct { + // Virtual host roles list. + Roles Roles `json:"roles"` +} + +// +kubebuilder:validation:Enum="nano";"micro";"small";"medium";"large";"xlarge";"2xlarge" +type ResourcesPreset string + +// +kubebuilder:validation:Enum="v4.2";"v4.1";"v4.0";"v3.13" +type Version string diff --git a/api/apps/v1alpha1/rabbitmq/zz_generated.deepcopy.go b/api/apps/v1alpha1/rabbitmq/zz_generated.deepcopy.go new file mode 100644 index 00000000..a1d55f89 --- /dev/null +++ b/api/apps/v1alpha1/rabbitmq/zz_generated.deepcopy.go @@ -0,0 +1,155 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package rabbitmq + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Config) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) + out.Size = in.Size.DeepCopy() + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make(map[string]User, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Vhosts != nil { + in, out := &in.Vhosts, &out.Vhosts + *out = make(map[string]Vhost, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. +func (in *ConfigSpec) DeepCopy() *ConfigSpec { + if in == nil { + return nil + } + out := new(ConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + out.Cpu = in.Cpu.DeepCopy() + out.Memory = in.Memory.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Roles) DeepCopyInto(out *Roles) { + *out = *in + if in.Admin != nil { + in, out := &in.Admin, &out.Admin + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Readonly != nil { + in, out := &in.Readonly, &out.Readonly + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Roles. +func (in *Roles) DeepCopy() *Roles { + if in == nil { + return nil + } + out := new(Roles) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *User) DeepCopyInto(out *User) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new User. +func (in *User) DeepCopy() *User { + if in == nil { + return nil + } + out := new(User) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Vhost) DeepCopyInto(out *Vhost) { + *out = *in + in.Roles.DeepCopyInto(&out.Roles) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Vhost. +func (in *Vhost) DeepCopy() *Vhost { + if in == nil { + return nil + } + out := new(Vhost) + in.DeepCopyInto(out) + return out +} diff --git a/api/apps/v1alpha1/redis/types.go b/api/apps/v1alpha1/redis/types.go new file mode 100644 index 00000000..e9a02c72 --- /dev/null +++ b/api/apps/v1alpha1/redis/types.go @@ -0,0 +1,57 @@ +// Code generated by values-gen. DO NOT EDIT. +// +kubebuilder:object:generate=true +// +groupName=apps.cozystack.io +// +versionName=v1alpha1 +package redis + +import ( + resource "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +type Config struct { + v1.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + Spec ConfigSpec `json:"spec,omitempty"` +} + +type ConfigSpec struct { + // Number of Redis replicas. + // +kubebuilder:default:=2 + Replicas int `json:"replicas"` + // Explicit CPU and memory configuration for each Redis replica. When omitted, the preset defined in `resourcesPreset` is applied. + // +kubebuilder:default:={} + Resources Resources `json:"resources,omitempty"` + // Default sizing preset used when `resources` is omitted. + // +kubebuilder:default:="nano" + ResourcesPreset ResourcesPreset `json:"resourcesPreset"` + // Persistent Volume Claim size available for application data. + // +kubebuilder:default:="1Gi" + Size resource.Quantity `json:"size"` + // StorageClass used to store the data. + // +kubebuilder:default:="" + StorageClass string `json:"storageClass"` + // Enable external access from outside the cluster. + // +kubebuilder:default:=false + External bool `json:"external"` + // Redis major version to deploy + // +kubebuilder:default:="v8" + Version Version `json:"version"` + // Enable password generation. + // +kubebuilder:default:=true + AuthEnabled bool `json:"authEnabled"` +} + +type Resources struct { + // CPU available to each replica. + Cpu resource.Quantity `json:"cpu,omitempty"` + // Memory (RAM) available to each replica. + Memory resource.Quantity `json:"memory,omitempty"` +} + +// +kubebuilder:validation:Enum="nano";"micro";"small";"medium";"large";"xlarge";"2xlarge" +type ResourcesPreset string + +// +kubebuilder:validation:Enum="v8";"v7" +type Version string diff --git a/api/apps/v1alpha1/redis/zz_generated.deepcopy.go b/api/apps/v1alpha1/redis/zz_generated.deepcopy.go new file mode 100644 index 00000000..6390200c --- /dev/null +++ b/api/apps/v1alpha1/redis/zz_generated.deepcopy.go @@ -0,0 +1,85 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package redis + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Config) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) + out.Size = in.Size.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. +func (in *ConfigSpec) DeepCopy() *ConfigSpec { + if in == nil { + return nil + } + out := new(ConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + out.Cpu = in.Cpu.DeepCopy() + out.Memory = in.Memory.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} diff --git a/api/apps/v1alpha1/tcpbalancer/types.go b/api/apps/v1alpha1/tcpbalancer/types.go new file mode 100644 index 00000000..aa99cd6d --- /dev/null +++ b/api/apps/v1alpha1/tcpbalancer/types.go @@ -0,0 +1,75 @@ +// Code generated by values-gen. DO NOT EDIT. +// +kubebuilder:object:generate=true +// +groupName=apps.cozystack.io +// +versionName=v1alpha1 +package tcpbalancer + +import ( + resource "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +type Config struct { + v1.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + Spec ConfigSpec `json:"spec,omitempty"` +} + +type ConfigSpec struct { + // Number of HAProxy replicas. + // +kubebuilder:default:=2 + Replicas int `json:"replicas"` + // Explicit CPU and memory configuration for each TCP Balancer replica. When omitted, the preset defined in `resourcesPreset` is applied. + // +kubebuilder:default:={} + Resources Resources `json:"resources,omitempty"` + // Default sizing preset used when `resources` is omitted. + // +kubebuilder:default:="nano" + ResourcesPreset ResourcesPreset `json:"resourcesPreset"` + // Enable external access from outside the cluster. + // +kubebuilder:default:=false + External bool `json:"external"` + // HTTP and HTTPS configuration. + // +kubebuilder:default:={} + HttpAndHttps HttpAndHttps `json:"httpAndHttps"` + // Secure HTTP by whitelisting client networks (default: false). + // +kubebuilder:default:=false + WhitelistHTTP bool `json:"whitelistHTTP"` + // List of allowed client networks. + // +kubebuilder:default:={} + Whitelist []string `json:"whitelist,omitempty"` +} + +type HttpAndHttps struct { + // Endpoint addresses list. + // +kubebuilder:default:={} + Endpoints []string `json:"endpoints,omitempty"` + // Mode for balancer. + // +kubebuilder:default:="tcp" + Mode Mode `json:"mode"` + // Target ports configuration. + // +kubebuilder:default:={} + TargetPorts TargetPorts `json:"targetPorts"` +} + +type Resources struct { + // CPU available to each replica. + Cpu resource.Quantity `json:"cpu,omitempty"` + // Memory (RAM) available to each replica. + Memory resource.Quantity `json:"memory,omitempty"` +} + +type TargetPorts struct { + // HTTP port number. + // +kubebuilder:default:=80 + Http int `json:"http"` + // HTTPS port number. + // +kubebuilder:default:=443 + Https int `json:"https"` +} + +// +kubebuilder:validation:Enum="tcp";"tcp-with-proxy" +type Mode string + +// +kubebuilder:validation:Enum="nano";"micro";"small";"medium";"large";"xlarge";"2xlarge" +type ResourcesPreset string diff --git a/api/apps/v1alpha1/tcpbalancer/zz_generated.deepcopy.go b/api/apps/v1alpha1/tcpbalancer/zz_generated.deepcopy.go new file mode 100644 index 00000000..1c63eafc --- /dev/null +++ b/api/apps/v1alpha1/tcpbalancer/zz_generated.deepcopy.go @@ -0,0 +1,126 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package tcpbalancer + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Config) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) + in.HttpAndHttps.DeepCopyInto(&out.HttpAndHttps) + if in.Whitelist != nil { + in, out := &in.Whitelist, &out.Whitelist + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. +func (in *ConfigSpec) DeepCopy() *ConfigSpec { + if in == nil { + return nil + } + out := new(ConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HttpAndHttps) DeepCopyInto(out *HttpAndHttps) { + *out = *in + if in.Endpoints != nil { + in, out := &in.Endpoints, &out.Endpoints + *out = make([]string, len(*in)) + copy(*out, *in) + } + out.TargetPorts = in.TargetPorts +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HttpAndHttps. +func (in *HttpAndHttps) DeepCopy() *HttpAndHttps { + if in == nil { + return nil + } + out := new(HttpAndHttps) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + out.Cpu = in.Cpu.DeepCopy() + out.Memory = in.Memory.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TargetPorts) DeepCopyInto(out *TargetPorts) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetPorts. +func (in *TargetPorts) DeepCopy() *TargetPorts { + if in == nil { + return nil + } + out := new(TargetPorts) + in.DeepCopyInto(out) + return out +} diff --git a/api/apps/v1alpha1/tenant/types.go b/api/apps/v1alpha1/tenant/types.go new file mode 100644 index 00000000..60f8bb22 --- /dev/null +++ b/api/apps/v1alpha1/tenant/types.go @@ -0,0 +1,41 @@ +// Code generated by values-gen. DO NOT EDIT. +// +kubebuilder:object:generate=true +// +groupName=apps.cozystack.io +// +versionName=v1alpha1 +package tenant + +import ( + resource "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +type Config struct { + v1.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + Spec ConfigSpec `json:"spec,omitempty"` +} + +type ConfigSpec struct { + // The hostname used to access tenant services (defaults to using the tenant name as a subdomain for its parent tenant host). + // +kubebuilder:default:="" + Host string `json:"host,omitempty"` + // Deploy own Etcd cluster. + // +kubebuilder:default:=false + Etcd bool `json:"etcd"` + // Deploy own Monitoring Stack. + // +kubebuilder:default:=false + Monitoring bool `json:"monitoring"` + // Deploy own Ingress Controller. + // +kubebuilder:default:=false + Ingress bool `json:"ingress"` + // Deploy own SeaweedFS. + // +kubebuilder:default:=false + Seaweedfs bool `json:"seaweedfs"` + // The name of a SchedulingClass CR to apply scheduling constraints for this tenant's workloads. + // +kubebuilder:default:="" + SchedulingClass string `json:"schedulingClass,omitempty"` + // Define resource quotas for the tenant. + // +kubebuilder:default:={} + ResourceQuotas map[string]resource.Quantity `json:"resourceQuotas,omitempty"` +} diff --git a/api/apps/v1alpha1/tenant/zz_generated.deepcopy.go b/api/apps/v1alpha1/tenant/zz_generated.deepcopy.go new file mode 100644 index 00000000..784a8f25 --- /dev/null +++ b/api/apps/v1alpha1/tenant/zz_generated.deepcopy.go @@ -0,0 +1,74 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package tenant + +import ( + "k8s.io/apimachinery/pkg/api/resource" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Config) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { + *out = *in + if in.ResourceQuotas != nil { + in, out := &in.ResourceQuotas, &out.ResourceQuotas + *out = make(map[string]resource.Quantity, len(*in)) + for key, val := range *in { + (*out)[key] = val.DeepCopy() + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. +func (in *ConfigSpec) DeepCopy() *ConfigSpec { + if in == nil { + return nil + } + out := new(ConfigSpec) + in.DeepCopyInto(out) + return out +} diff --git a/api/apps/v1alpha1/vmdisk/types.go b/api/apps/v1alpha1/vmdisk/types.go new file mode 100644 index 00000000..c493f49c --- /dev/null +++ b/api/apps/v1alpha1/vmdisk/types.go @@ -0,0 +1,61 @@ +// Code generated by values-gen. DO NOT EDIT. +// +kubebuilder:object:generate=true +// +groupName=apps.cozystack.io +// +versionName=v1alpha1 +package vmdisk + +import ( + resource "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +type Config struct { + v1.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + Spec ConfigSpec `json:"spec,omitempty"` +} + +type ConfigSpec struct { + // The source image location used to create a disk. + // +kubebuilder:default:={} + Source Source `json:"source"` + // Defines if disk should be considered optical. + // +kubebuilder:default:=false + Optical bool `json:"optical"` + // The size of the disk allocated for the virtual machine. + // +kubebuilder:default:="5Gi" + Storage resource.Quantity `json:"storage"` + // StorageClass used to store the data. + // +kubebuilder:default:="replicated" + StorageClass string `json:"storageClass"` +} + +type Source struct { + // Clone an existing vm-disk. + Disk *SourceDisk `json:"disk,omitempty"` + // Download image from an HTTP source. + Http *SourceHTTP `json:"http,omitempty"` + // Use image by name from default collection. + Image *SourceImage `json:"image,omitempty"` + // Upload local image. + Upload *SourceUpload `json:"upload,omitempty"` +} + +type SourceDisk struct { + // Name of the vm-disk to clone. + Name string `json:"name"` +} + +type SourceHTTP struct { + // URL to download the image. + Url string `json:"url"` +} + +type SourceImage struct { + // Name of the image to use. + Name string `json:"name"` +} + +type SourceUpload struct { +} diff --git a/api/apps/v1alpha1/vmdisk/zz_generated.deepcopy.go b/api/apps/v1alpha1/vmdisk/zz_generated.deepcopy.go new file mode 100644 index 00000000..301bb1d9 --- /dev/null +++ b/api/apps/v1alpha1/vmdisk/zz_generated.deepcopy.go @@ -0,0 +1,163 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package vmdisk + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Config) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { + *out = *in + in.Source.DeepCopyInto(&out.Source) + out.Storage = in.Storage.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. +func (in *ConfigSpec) DeepCopy() *ConfigSpec { + if in == nil { + return nil + } + out := new(ConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Source) DeepCopyInto(out *Source) { + *out = *in + if in.Disk != nil { + in, out := &in.Disk, &out.Disk + *out = new(SourceDisk) + **out = **in + } + if in.Http != nil { + in, out := &in.Http, &out.Http + *out = new(SourceHTTP) + **out = **in + } + if in.Image != nil { + in, out := &in.Image, &out.Image + *out = new(SourceImage) + **out = **in + } + if in.Upload != nil { + in, out := &in.Upload, &out.Upload + *out = new(SourceUpload) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Source. +func (in *Source) DeepCopy() *Source { + if in == nil { + return nil + } + out := new(Source) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SourceDisk) DeepCopyInto(out *SourceDisk) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceDisk. +func (in *SourceDisk) DeepCopy() *SourceDisk { + if in == nil { + return nil + } + out := new(SourceDisk) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SourceHTTP) DeepCopyInto(out *SourceHTTP) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceHTTP. +func (in *SourceHTTP) DeepCopy() *SourceHTTP { + if in == nil { + return nil + } + out := new(SourceHTTP) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SourceImage) DeepCopyInto(out *SourceImage) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceImage. +func (in *SourceImage) DeepCopy() *SourceImage { + if in == nil { + return nil + } + out := new(SourceImage) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SourceUpload) DeepCopyInto(out *SourceUpload) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceUpload. +func (in *SourceUpload) DeepCopy() *SourceUpload { + if in == nil { + return nil + } + out := new(SourceUpload) + in.DeepCopyInto(out) + return out +} diff --git a/api/apps/v1alpha1/vminstance/types.go b/api/apps/v1alpha1/vminstance/types.go new file mode 100644 index 00000000..9c48724a --- /dev/null +++ b/api/apps/v1alpha1/vminstance/types.go @@ -0,0 +1,100 @@ +// Code generated by values-gen. DO NOT EDIT. +// +kubebuilder:object:generate=true +// +groupName=apps.cozystack.io +// +versionName=v1alpha1 +package vminstance + +import ( + resource "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +type Config struct { + v1.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + Spec ConfigSpec `json:"spec,omitempty"` +} + +type ConfigSpec struct { + // Enable external access from outside the cluster. + // +kubebuilder:default:=false + External bool `json:"external"` + // Method to pass through traffic to the VM. + // +kubebuilder:default:="PortList" + ExternalMethod ExternalMethod `json:"externalMethod"` + // Ports to forward from outside the cluster. + // +kubebuilder:default:={22} + ExternalPorts []int `json:"externalPorts,omitempty"` + // Whether to accept ICMP traffic to the VM in PortList mode (preserves ping and PMTU discovery). No effect in WholeIP mode. Default true so ping behaves as users expect even when port filtering is in effect. + // +kubebuilder:default:=true + ExternalAllowICMP bool `json:"externalAllowICMP"` + // Requested running state of the VirtualMachineInstance + // +kubebuilder:default:="Always" + RunStrategy RunStrategy `json:"runStrategy"` + // Virtual Machine instance type. + // +kubebuilder:default:="u1.medium" + InstanceType string `json:"instanceType"` + // Virtual Machine preferences profile. + // +kubebuilder:default:="ubuntu" + InstanceProfile string `json:"instanceProfile"` + // List of disks to attach. + // +kubebuilder:default:={} + Disks []Disk `json:"disks,omitempty"` + // Networks to attach the VM to. + // +kubebuilder:default:={} + Networks []Network `json:"networks,omitempty"` + // Deprecated: use networks instead. + // +kubebuilder:default:={} + Subnets []Network `json:"subnets,omitempty"` + // List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM). + // +kubebuilder:default:={} + Gpus []GPU `json:"gpus,omitempty"` + // Model specifies the CPU model inside the VMI. List of available models https://github.com/libvirt/libvirt/tree/master/src/cpu_map + // +kubebuilder:default:="" + CpuModel string `json:"cpuModel"` + // Resource configuration for the virtual machine. + // +kubebuilder:default:={} + Resources Resources `json:"resources,omitempty"` + // List of SSH public keys for authentication. + // +kubebuilder:default:={} + SshKeys []string `json:"sshKeys,omitempty"` + // Cloud-init user data. + // +kubebuilder:default:="" + CloudInit string `json:"cloudInit"` + // Seed string to generate SMBIOS UUID for the VM. + // +kubebuilder:default:="" + CloudInitSeed string `json:"cloudInitSeed"` +} + +type Disk struct { + // Disk bus type (e.g. "sata"). + Bus string `json:"bus,omitempty"` + // Disk name. + Name string `json:"name"` +} + +type GPU struct { + // The name of the GPU resource to attach. + Name string `json:"name"` +} + +type Network struct { + // Network attachment name. + Name string `json:"name,omitempty"` +} + +type Resources struct { + // Number of CPU cores allocated. + Cpu resource.Quantity `json:"cpu,omitempty"` + // Amount of memory allocated. + Memory resource.Quantity `json:"memory,omitempty"` + // Number of CPU sockets (vCPU topology). + Sockets resource.Quantity `json:"sockets,omitempty"` +} + +// +kubebuilder:validation:Enum="PortList";"WholeIP" +type ExternalMethod string + +// +kubebuilder:validation:Enum="Always";"Halted";"Manual";"RerunOnFailure";"Once" +type RunStrategy string diff --git a/api/apps/v1alpha1/vminstance/zz_generated.deepcopy.go b/api/apps/v1alpha1/vminstance/zz_generated.deepcopy.go new file mode 100644 index 00000000..86d5ddfc --- /dev/null +++ b/api/apps/v1alpha1/vminstance/zz_generated.deepcopy.go @@ -0,0 +1,160 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package vminstance + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Config) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { + *out = *in + if in.ExternalPorts != nil { + in, out := &in.ExternalPorts, &out.ExternalPorts + *out = make([]int, len(*in)) + copy(*out, *in) + } + if in.Disks != nil { + in, out := &in.Disks, &out.Disks + *out = make([]Disk, len(*in)) + copy(*out, *in) + } + if in.Networks != nil { + in, out := &in.Networks, &out.Networks + *out = make([]Network, len(*in)) + copy(*out, *in) + } + if in.Subnets != nil { + in, out := &in.Subnets, &out.Subnets + *out = make([]Network, len(*in)) + copy(*out, *in) + } + if in.Gpus != nil { + in, out := &in.Gpus, &out.Gpus + *out = make([]GPU, len(*in)) + copy(*out, *in) + } + in.Resources.DeepCopyInto(&out.Resources) + if in.SshKeys != nil { + in, out := &in.SshKeys, &out.SshKeys + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. +func (in *ConfigSpec) DeepCopy() *ConfigSpec { + if in == nil { + return nil + } + out := new(ConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Disk) DeepCopyInto(out *Disk) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Disk. +func (in *Disk) DeepCopy() *Disk { + if in == nil { + return nil + } + out := new(Disk) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GPU) DeepCopyInto(out *GPU) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GPU. +func (in *GPU) DeepCopy() *GPU { + if in == nil { + return nil + } + out := new(GPU) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Network) DeepCopyInto(out *Network) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Network. +func (in *Network) DeepCopy() *Network { + if in == nil { + return nil + } + out := new(Network) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + out.Cpu = in.Cpu.DeepCopy() + out.Memory = in.Memory.DeepCopy() + out.Sockets = in.Sockets.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} diff --git a/api/apps/v1alpha1/vpc/types.go b/api/apps/v1alpha1/vpc/types.go new file mode 100644 index 00000000..fed72f58 --- /dev/null +++ b/api/apps/v1alpha1/vpc/types.go @@ -0,0 +1,49 @@ +// Code generated by values-gen. DO NOT EDIT. +// +kubebuilder:object:generate=true +// +groupName=apps.cozystack.io +// +versionName=v1alpha1 +package vpc + +import ( + "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +type Config struct { + v1.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + Spec ConfigSpec `json:"spec,omitempty"` +} + +type ConfigSpec struct { + // Subnets of a VPC + // +kubebuilder:default:={} + Subnets []Subnet `json:"subnets,omitempty"` + // VPC peering connections (bidirectional declaration required) + // +kubebuilder:default:={} + Peers []Peer `json:"peers,omitempty"` + // Static routes for the VPC + // +kubebuilder:default:={} + Routes []Route `json:"routes,omitempty"` +} + +type Peer struct { + // Namespace of the remote tenant + TenantNamespace string `json:"tenantNamespace"` + // Logical name of the remote VPC (without "virtualprivatecloud-" prefix) + VpcName string `json:"vpcName"` +} + +type Route struct { + // Destination CIDR + Cidr string `json:"cidr"` + // Next hop IP address + NextHopIP string `json:"nextHopIP"` +} + +type Subnet struct { + // IP address range + Cidr string `json:"cidr,omitempty"` + // Subnet name + Name string `json:"name"` +} diff --git a/api/apps/v1alpha1/vpc/zz_generated.deepcopy.go b/api/apps/v1alpha1/vpc/zz_generated.deepcopy.go new file mode 100644 index 00000000..3cda29c4 --- /dev/null +++ b/api/apps/v1alpha1/vpc/zz_generated.deepcopy.go @@ -0,0 +1,126 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package vpc + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Config) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { + *out = *in + if in.Subnets != nil { + in, out := &in.Subnets, &out.Subnets + *out = make([]Subnet, len(*in)) + copy(*out, *in) + } + if in.Peers != nil { + in, out := &in.Peers, &out.Peers + *out = make([]Peer, len(*in)) + copy(*out, *in) + } + if in.Routes != nil { + in, out := &in.Routes, &out.Routes + *out = make([]Route, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. +func (in *ConfigSpec) DeepCopy() *ConfigSpec { + if in == nil { + return nil + } + out := new(ConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Peer) DeepCopyInto(out *Peer) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Peer. +func (in *Peer) DeepCopy() *Peer { + if in == nil { + return nil + } + out := new(Peer) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Route) DeepCopyInto(out *Route) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Route. +func (in *Route) DeepCopy() *Route { + if in == nil { + return nil + } + out := new(Route) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Subnet) DeepCopyInto(out *Subnet) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Subnet. +func (in *Subnet) DeepCopy() *Subnet { + if in == nil { + return nil + } + out := new(Subnet) + in.DeepCopyInto(out) + return out +} diff --git a/api/apps/v1alpha1/vpn/types.go b/api/apps/v1alpha1/vpn/types.go new file mode 100644 index 00000000..7ec0b0e3 --- /dev/null +++ b/api/apps/v1alpha1/vpn/types.go @@ -0,0 +1,56 @@ +// Code generated by values-gen. DO NOT EDIT. +// +kubebuilder:object:generate=true +// +groupName=apps.cozystack.io +// +versionName=v1alpha1 +package vpn + +import ( + resource "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +type Config struct { + v1.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + Spec ConfigSpec `json:"spec,omitempty"` +} + +type ConfigSpec struct { + // Number of VPN server replicas. + // +kubebuilder:default:=2 + Replicas int `json:"replicas"` + // Explicit CPU and memory configuration for each VPN server replica. When omitted, the preset defined in `resourcesPreset` is applied. + // +kubebuilder:default:={} + Resources Resources `json:"resources,omitempty"` + // Default sizing preset used when `resources` is omitted. + // +kubebuilder:default:="nano" + ResourcesPreset ResourcesPreset `json:"resourcesPreset"` + // Enable external access from outside the cluster. + // +kubebuilder:default:=false + External bool `json:"external"` + // Host used to substitute into generated URLs. + // +kubebuilder:default:="" + Host string `json:"host"` + // Users configuration map. + // +kubebuilder:default:={} + Users map[string]User `json:"users,omitempty"` + // List of externalIPs for service. Optional. If not specified, will use LoadBalancer service by default. + // +kubebuilder:default:={} + ExternalIPs []string `json:"externalIPs,omitempty"` +} + +type Resources struct { + // CPU available to each replica. + Cpu resource.Quantity `json:"cpu,omitempty"` + // Memory (RAM) available to each replica. + Memory resource.Quantity `json:"memory,omitempty"` +} + +type User struct { + // Password for the user (autogenerated if not provided). + Password string `json:"password,omitempty"` +} + +// +kubebuilder:validation:Enum="nano";"micro";"small";"medium";"large";"xlarge";"2xlarge" +type ResourcesPreset string diff --git a/api/apps/v1alpha1/vpn/zz_generated.deepcopy.go b/api/apps/v1alpha1/vpn/zz_generated.deepcopy.go new file mode 100644 index 00000000..a3595bb0 --- /dev/null +++ b/api/apps/v1alpha1/vpn/zz_generated.deepcopy.go @@ -0,0 +1,111 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package vpn + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Config) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make(map[string]User, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.ExternalIPs != nil { + in, out := &in.ExternalIPs, &out.ExternalIPs + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. +func (in *ConfigSpec) DeepCopy() *ConfigSpec { + if in == nil { + return nil + } + out := new(ConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + out.Cpu = in.Cpu.DeepCopy() + out.Memory = in.Memory.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *User) DeepCopyInto(out *User) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new User. +func (in *User) DeepCopy() *User { + if in == nil { + return nil + } + out := new(User) + in.DeepCopyInto(out) + return out +} diff --git a/api/backups/v1alpha1/backup_types.go b/api/backups/v1alpha1/backup_types.go index 9371c27f..e46212f6 100644 --- a/api/backups/v1alpha1/backup_types.go +++ b/api/backups/v1alpha1/backup_types.go @@ -72,6 +72,15 @@ type BackupSpec struct { DriverMetadata map[string]string `json:"driverMetadata,omitempty"` } +// DataVolumeResource describes a dataVolume associated with the backed-up application. +type DataVolumeResource struct { + // DataVolumeName is the name of the dataVolume/PVC (e.g., "vm-disk-ubuntu-source"). + DataVolumeName string `json:"dataVolumeName"` + + // ApplicationName is the cozystack application name for this disk (e.g., "ubuntu-source"). + ApplicationName string `json:"applicationName"` +} + // BackupStatus represents the observed state of a Backup. type BackupStatus struct { // Phase is a simple, high-level summary of the backup's state. @@ -83,6 +92,15 @@ type BackupStatus struct { // +optional Artifact *BackupArtifact `json:"artifact,omitempty"` + // UnderlyingResources holds application-specific resource metadata discovered + // during backup (e.g., VM disks, network configuration). The payload is a + // self-typed JSON object carrying an inlined TypeMeta (kind/apiVersion) so + // the consuming controller can dispatch on the application kind. + // +optional + // +kubebuilder:pruning:PreserveUnknownFields + // +kubebuilder:validation:Type=object + UnderlyingResources *runtime.RawExtension `json:"underlyingResources,omitempty"` + // Conditions represents the latest available observations of a Backup's state. // +optional Conditions []metav1.Condition `json:"conditions,omitempty"` diff --git a/api/backups/v1alpha1/restorejob_types.go b/api/backups/v1alpha1/restorejob_types.go index 1a2dfa00..9817e8c1 100644 --- a/api/backups/v1alpha1/restorejob_types.go +++ b/api/backups/v1alpha1/restorejob_types.go @@ -42,6 +42,12 @@ type RestoreJobSpec struct { // application as referenced by backup.spec.applicationRef. // +optional TargetApplicationRef *corev1.TypedLocalObjectReference `json:"targetApplicationRef,omitempty"` + + // Options is a driver-specific blob of restore options, typed based on + // targetApplicationRef and the current controller implementation. + // +optional + // +kubebuilder:pruning:PreserveUnknownFields + Options *runtime.RawExtension `json:"options,omitempty"` } // RestoreJobStatus represents the observed state of a RestoreJob. diff --git a/api/backups/v1alpha1/zz_generated.deepcopy.go b/api/backups/v1alpha1/zz_generated.deepcopy.go index b2e4a680..61b8f839 100644 --- a/api/backups/v1alpha1/zz_generated.deepcopy.go +++ b/api/backups/v1alpha1/zz_generated.deepcopy.go @@ -400,6 +400,11 @@ func (in *BackupStatus) DeepCopyInto(out *BackupStatus) { *out = new(BackupArtifact) **out = **in } + if in.UnderlyingResources != nil { + in, out := &in.UnderlyingResources, &out.UnderlyingResources + *out = new(runtime.RawExtension) + (*in).DeepCopyInto(*out) + } if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions *out = make([]metav1.Condition, len(*in)) @@ -419,6 +424,21 @@ func (in *BackupStatus) DeepCopy() *BackupStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DataVolumeResource) DeepCopyInto(out *DataVolumeResource) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataVolumeResource. +func (in *DataVolumeResource) DeepCopy() *DataVolumeResource { + if in == nil { + return nil + } + out := new(DataVolumeResource) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Plan) DeepCopyInto(out *Plan) { *out = *in @@ -600,6 +620,11 @@ func (in *RestoreJobSpec) DeepCopyInto(out *RestoreJobSpec) { *out = new(v1.TypedLocalObjectReference) (*in).DeepCopyInto(*out) } + if in.Options != nil { + in, out := &in.Options, &out.Options + *out = new(runtime.RawExtension) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RestoreJobSpec. diff --git a/api/v1alpha1/packagesource_types.go b/api/v1alpha1/packagesource_types.go index c0b943e8..75bf217b 100644 --- a/api/v1alpha1/packagesource_types.go +++ b/api/v1alpha1/packagesource_types.go @@ -132,6 +132,16 @@ type ComponentInstall struct { // DependsOn is a list of component names that must be installed before this component // +optional DependsOn []string `json:"dependsOn,omitempty"` + + // UpgradeCRDs controls how CRDs from the chart's crds/ directory are + // handled on HelmRelease upgrades. Maps to HelmRelease.Spec.Upgrade.CRDs. + // Empty string (default) preserves the helm-controller default (Skip). + // Use "CreateReplace" for operators that evolve their CRD set between + // versions. Warning: CreateReplace overwrites CRDs and may cause data + // loss if upstream drops fields from a CRD with live objects. + // +optional + // +kubebuilder:validation:Enum=Skip;Create;CreateReplace + UpgradeCRDs string `json:"upgradeCRDs,omitempty"` } // Component defines a single Helm release component within a package source diff --git a/cmd/backupstrategy-controller/main.go b/cmd/backupstrategy-controller/main.go index 6c0ee2e6..03396979 100644 --- a/cmd/backupstrategy-controller/main.go +++ b/cmd/backupstrategy-controller/main.go @@ -176,6 +176,15 @@ func main() { os.Exit(1) } + if err = (&backupcontroller.BackupReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Recorder: mgr.GetEventRecorderFor("backup-controller"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Backup") + os.Exit(1) + } + // +kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { diff --git a/cmd/cozystack-controller/main.go b/cmd/cozystack-controller/main.go index 77c4f9dc..bcaca9c9 100644 --- a/cmd/cozystack-controller/main.go +++ b/cmd/cozystack-controller/main.go @@ -42,6 +42,7 @@ import ( "github.com/cozystack/cozystack/internal/telemetry" helmv2 "github.com/fluxcd/helm-controller/api/v2" + cosiv1alpha1 "sigs.k8s.io/container-object-storage-interface-api/apis/objectstorage/v1alpha1" // +kubebuilder:scaffold:imports ) @@ -56,6 +57,7 @@ func init() { utilruntime.Must(cozystackiov1alpha1.AddToScheme(scheme)) utilruntime.Must(dashboard.AddToScheme(scheme)) utilruntime.Must(helmv2.AddToScheme(scheme)) + utilruntime.Must(cosiv1alpha1.AddToScheme(scheme)) // +kubebuilder:scaffold:scheme } diff --git a/cmd/cozystack-operator/main.go b/cmd/cozystack-operator/main.go index d3c40f7e..e215b779 100644 --- a/cmd/cozystack-operator/main.go +++ b/cmd/cozystack-operator/main.go @@ -108,7 +108,7 @@ func main() { flag.StringVar(&telemetryInterval, "telemetry-interval", "15m", "Interval between telemetry data collection (e.g. 15m, 1h)") flag.StringVar(&platformSourceURL, "platform-source-url", "", "Platform source URL (oci:// or https://). If specified, generates OCIRepository or GitRepository resource.") - flag.StringVar(&platformSourceName, "platform-source-name", "cozystack-packages", "Name for the generated platform source resource (default: cozystack-packages)") + flag.StringVar(&platformSourceName, "platform-source-name", "cozystack-platform", "Name for the generated platform source resource and PackageSource") flag.StringVar(&platformSourceRef, "platform-source-ref", "", "Reference specification as key=value pairs (e.g., 'branch=main' or 'digest=sha256:...,tag=v1.0'). For OCI: digest, semver, semverFilter, tag. For Git: branch, tag, semver, name, commit.") flag.StringVar(&cozyValuesSecretName, "cozy-values-secret-name", "cozystack-values", "The name of the secret containing cluster-wide configuration values.") flag.StringVar(&cozyValuesSecretNamespace, "cozy-values-secret-namespace", "cozy-system", "The namespace of the secret containing cluster-wide configuration values.") @@ -224,6 +224,29 @@ func main() { } } + // Create platform PackageSource when CRDs are managed by the operator and + // a platform source URL is configured. Without a URL there is no Flux source + // resource to reference, so creating a PackageSource would leave a dangling SourceRef. + if installCRDs && platformSourceURL != "" { + sourceRefKind := "OCIRepository" + sourceType, _, err := parsePlatformSourceURL(platformSourceURL) + if err != nil { + setupLog.Error(err, "failed to parse platform source URL for PackageSource") + os.Exit(1) + } + if sourceType == "git" { + sourceRefKind = "GitRepository" + } + setupLog.Info("Creating platform PackageSource", "platformSourceName", platformSourceName) + psCtx, psCancel := context.WithTimeout(mgrCtx, 2*time.Minute) + defer psCancel() + if err := installPlatformPackageSource(psCtx, directClient, platformSourceName, sourceRefKind); err != nil { + setupLog.Error(err, "failed to create platform PackageSource") + os.Exit(1) + } + setupLog.Info("Platform PackageSource creation completed successfully") + } + // Setup PackageSource reconciler if err := (&operator.PackageSourceReconciler{ Client: mgr.GetClient(), @@ -552,3 +575,79 @@ func generateGitRepository(name, repoURL string, refMap map[string]string) (*sou return obj, nil } + +// installPlatformPackageSource creates the platform PackageSource resource +// that references the Flux source resource (OCIRepository or GitRepository). +// +// The variant list is intentionally hardcoded here. These are platform-defined +// deployment profiles (not user-extensible), matching what was previously in +// the Helm template. Changes require a new operator build and release. +func installPlatformPackageSource(ctx context.Context, k8sClient client.Client, platformSourceName, sourceRefKind string) error { + logger := log.FromContext(ctx) + + packageSourceName := "cozystack." + platformSourceName + + ps := &cozyv1alpha1.PackageSource{ + TypeMeta: metav1.TypeMeta{ + APIVersion: cozyv1alpha1.GroupVersion.String(), + Kind: "PackageSource", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: packageSourceName, + Annotations: map[string]string{ + "operator.cozystack.io/skip-cozystack-values": "true", + }, + }, + Spec: cozyv1alpha1.PackageSourceSpec{ + SourceRef: &cozyv1alpha1.PackageSourceRef{ + Kind: sourceRefKind, + Name: platformSourceName, + Namespace: "cozy-system", + Path: "/", + }, + }, + } + + variantData := []struct { + name string + valuesFiles []string + }{ + {"default", []string{"values.yaml"}}, + {"isp-full", []string{"values.yaml", "values-isp-full.yaml"}}, + {"isp-hosted", []string{"values.yaml", "values-isp-hosted.yaml"}}, + {"isp-full-generic", []string{"values.yaml", "values-isp-full-generic.yaml"}}, + } + + variants := make([]cozyv1alpha1.Variant, len(variantData)) + for i, v := range variantData { + variants[i] = cozyv1alpha1.Variant{ + Name: v.name, + Components: []cozyv1alpha1.Component{ + { + Name: "platform", + Path: "core/platform", + Install: &cozyv1alpha1.ComponentInstall{ + Namespace: "cozy-system", + ReleaseName: "cozystack-platform", + }, + ValuesFiles: v.valuesFiles, + }, + }, + } + } + ps.Spec.Variants = variants + + logger.Info("Applying platform PackageSource", "name", packageSourceName) + + patchOptions := &client.PatchOptions{ + FieldManager: "cozystack-operator", + Force: func() *bool { b := true; return &b }(), + } + + if err := k8sClient.Patch(ctx, ps, client.Apply, patchOptions); err != nil { + return fmt.Errorf("failed to apply PackageSource %s: %w", packageSourceName, err) + } + + logger.Info("Applied platform PackageSource", "name", packageSourceName) + return nil +} diff --git a/cmd/cozystack-operator/main_test.go b/cmd/cozystack-operator/main_test.go new file mode 100644 index 00000000..a69bc9b5 --- /dev/null +++ b/cmd/cozystack-operator/main_test.go @@ -0,0 +1,574 @@ +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "context" + "testing" + + cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +func newTestScheme() *runtime.Scheme { + s := runtime.NewScheme() + _ = cozyv1alpha1.AddToScheme(s) + return s +} + +func TestInstallPlatformPackageSource_Creates(t *testing.T) { + s := newTestScheme() + k8sClient := fake.NewClientBuilder().WithScheme(s).Build() + + err := installPlatformPackageSource(context.Background(), k8sClient, "cozystack-platform", "OCIRepository") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + ps := &cozyv1alpha1.PackageSource{} + if err := k8sClient.Get(context.Background(), client.ObjectKey{Name: "cozystack.cozystack-platform"}, ps); err != nil { + t.Fatalf("PackageSource not found: %v", err) + } + + // Verify name + if ps.Name != "cozystack.cozystack-platform" { + t.Errorf("expected name %q, got %q", "cozystack.cozystack-platform", ps.Name) + } + + // Verify annotation + if ps.Annotations["operator.cozystack.io/skip-cozystack-values"] != "true" { + t.Errorf("expected skip-cozystack-values annotation to be 'true', got %q", ps.Annotations["operator.cozystack.io/skip-cozystack-values"]) + } + + // Verify sourceRef + if ps.Spec.SourceRef == nil { + t.Fatal("expected SourceRef to be set") + } + if ps.Spec.SourceRef.Kind != "OCIRepository" { + t.Errorf("expected sourceRef.kind %q, got %q", "OCIRepository", ps.Spec.SourceRef.Kind) + } + if ps.Spec.SourceRef.Name != "cozystack-platform" { + t.Errorf("expected sourceRef.name %q, got %q", "cozystack-platform", ps.Spec.SourceRef.Name) + } + if ps.Spec.SourceRef.Namespace != "cozy-system" { + t.Errorf("expected sourceRef.namespace %q, got %q", "cozy-system", ps.Spec.SourceRef.Namespace) + } + if ps.Spec.SourceRef.Path != "/" { + t.Errorf("expected sourceRef.path %q, got %q", "/", ps.Spec.SourceRef.Path) + } + + // Verify variants + expectedVariants := []string{"default", "isp-full", "isp-hosted", "isp-full-generic"} + if len(ps.Spec.Variants) != len(expectedVariants) { + t.Fatalf("expected %d variants, got %d", len(expectedVariants), len(ps.Spec.Variants)) + } + for i, name := range expectedVariants { + if ps.Spec.Variants[i].Name != name { + t.Errorf("expected variant[%d].name %q, got %q", i, name, ps.Spec.Variants[i].Name) + } + if len(ps.Spec.Variants[i].Components) != 1 { + t.Errorf("expected variant[%d] to have 1 component, got %d", i, len(ps.Spec.Variants[i].Components)) + } + } +} + +func TestInstallPlatformPackageSource_Updates(t *testing.T) { + s := newTestScheme() + + existing := &cozyv1alpha1.PackageSource{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cozystack.cozystack-platform", + ResourceVersion: "1", + Labels: map[string]string{ + "custom-label": "should-be-preserved", + }, + }, + Spec: cozyv1alpha1.PackageSourceSpec{ + SourceRef: &cozyv1alpha1.PackageSourceRef{ + Kind: "OCIRepository", + Name: "old-name", + Namespace: "cozy-system", + }, + }, + } + + k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(existing).Build() + + err := installPlatformPackageSource(context.Background(), k8sClient, "cozystack-platform", "OCIRepository") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + ps := &cozyv1alpha1.PackageSource{} + if err := k8sClient.Get(context.Background(), client.ObjectKey{Name: "cozystack.cozystack-platform"}, ps); err != nil { + t.Fatalf("PackageSource not found: %v", err) + } + + // Verify sourceRef was updated + if ps.Spec.SourceRef.Name != "cozystack-platform" { + t.Errorf("expected updated sourceRef.name %q, got %q", "cozystack-platform", ps.Spec.SourceRef.Name) + } + + // Verify all 4 variants are present after update + if len(ps.Spec.Variants) != 4 { + t.Errorf("expected 4 variants after update, got %d", len(ps.Spec.Variants)) + } + + // Verify that labels set by other controllers are preserved (SSA does not overwrite unmanaged fields) + if ps.Labels["custom-label"] != "should-be-preserved" { + t.Errorf("expected custom-label to be preserved, got %q", ps.Labels["custom-label"]) + } +} + +func TestParsePlatformSourceURL(t *testing.T) { + tests := []struct { + name string + url string + wantType string + wantURL string + wantErr bool + }{ + { + name: "OCI URL", + url: "oci://ghcr.io/cozystack/cozystack/cozystack-packages", + wantType: "oci", + wantURL: "oci://ghcr.io/cozystack/cozystack/cozystack-packages", + }, + { + name: "HTTPS URL", + url: "https://github.com/cozystack/cozystack", + wantType: "git", + wantURL: "https://github.com/cozystack/cozystack", + }, + { + name: "SSH URL", + url: "ssh://git@github.com/cozystack/cozystack", + wantType: "git", + wantURL: "ssh://git@github.com/cozystack/cozystack", + }, + { + name: "empty URL", + url: "", + wantErr: true, + }, + { + name: "unsupported scheme", + url: "ftp://example.com/repo", + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + sourceType, repoURL, err := parsePlatformSourceURL(tt.url) + if tt.wantErr { + if err == nil { + t.Fatalf("expected error for URL %q, got nil", tt.url) + } + return + } + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if sourceType != tt.wantType { + t.Errorf("expected type %q, got %q", tt.wantType, sourceType) + } + if repoURL != tt.wantURL { + t.Errorf("expected URL %q, got %q", tt.wantURL, repoURL) + } + }) + } +} + +func TestInstallPlatformPackageSource_VariantValuesFiles(t *testing.T) { + s := newTestScheme() + k8sClient := fake.NewClientBuilder().WithScheme(s).Build() + + err := installPlatformPackageSource(context.Background(), k8sClient, "cozystack-platform", "OCIRepository") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + ps := &cozyv1alpha1.PackageSource{} + if err := k8sClient.Get(context.Background(), client.ObjectKey{Name: "cozystack.cozystack-platform"}, ps); err != nil { + t.Fatalf("PackageSource not found: %v", err) + } + + expectedValuesFiles := map[string][]string{ + "default": {"values.yaml"}, + "isp-full": {"values.yaml", "values-isp-full.yaml"}, + "isp-hosted": {"values.yaml", "values-isp-hosted.yaml"}, + "isp-full-generic": {"values.yaml", "values-isp-full-generic.yaml"}, + } + + for _, v := range ps.Spec.Variants { + expected, ok := expectedValuesFiles[v.Name] + if !ok { + t.Errorf("unexpected variant %q", v.Name) + continue + } + + if len(v.Components) != 1 { + t.Errorf("variant %q: expected 1 component, got %d", v.Name, len(v.Components)) + continue + } + + comp := v.Components[0] + if comp.Name != "platform" { + t.Errorf("variant %q: expected component name %q, got %q", v.Name, "platform", comp.Name) + } + if comp.Path != "core/platform" { + t.Errorf("variant %q: expected component path %q, got %q", v.Name, "core/platform", comp.Path) + } + if comp.Install == nil { + t.Errorf("variant %q: expected Install to be set", v.Name) + } else { + if comp.Install.Namespace != "cozy-system" { + t.Errorf("variant %q: expected install namespace %q, got %q", v.Name, "cozy-system", comp.Install.Namespace) + } + if comp.Install.ReleaseName != "cozystack-platform" { + t.Errorf("variant %q: expected install releaseName %q, got %q", v.Name, "cozystack-platform", comp.Install.ReleaseName) + } + } + + if len(comp.ValuesFiles) != len(expected) { + t.Errorf("variant %q: expected %d valuesFiles, got %d", v.Name, len(expected), len(comp.ValuesFiles)) + continue + } + for i, f := range expected { + if comp.ValuesFiles[i] != f { + t.Errorf("variant %q: expected valuesFiles[%d] %q, got %q", v.Name, i, f, comp.ValuesFiles[i]) + } + } + } +} + +func TestInstallPlatformPackageSource_CustomName(t *testing.T) { + s := newTestScheme() + k8sClient := fake.NewClientBuilder().WithScheme(s).Build() + + err := installPlatformPackageSource(context.Background(), k8sClient, "custom-source", "OCIRepository") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + ps := &cozyv1alpha1.PackageSource{} + if err := k8sClient.Get(context.Background(), client.ObjectKey{Name: "cozystack.custom-source"}, ps); err != nil { + t.Fatalf("PackageSource not found: %v", err) + } + + if ps.Name != "cozystack.custom-source" { + t.Errorf("expected name %q, got %q", "cozystack.custom-source", ps.Name) + } + if ps.Spec.SourceRef.Name != "custom-source" { + t.Errorf("expected sourceRef.name %q, got %q", "custom-source", ps.Spec.SourceRef.Name) + } +} + +func TestInstallPlatformPackageSource_GitRepository(t *testing.T) { + s := newTestScheme() + k8sClient := fake.NewClientBuilder().WithScheme(s).Build() + + err := installPlatformPackageSource(context.Background(), k8sClient, "my-source", "GitRepository") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + ps := &cozyv1alpha1.PackageSource{} + if err := k8sClient.Get(context.Background(), client.ObjectKey{Name: "cozystack.my-source"}, ps); err != nil { + t.Fatalf("PackageSource not found: %v", err) + } + + if ps.Spec.SourceRef.Kind != "GitRepository" { + t.Errorf("expected sourceRef.kind %q, got %q", "GitRepository", ps.Spec.SourceRef.Kind) + } + if ps.Spec.SourceRef.Name != "my-source" { + t.Errorf("expected sourceRef.name %q, got %q", "my-source", ps.Spec.SourceRef.Name) + } +} + +func TestParseRefSpec(t *testing.T) { + tests := []struct { + name string + input string + want map[string]string + wantErr bool + }{ + { + name: "empty string", + input: "", + want: map[string]string{}, + }, + { + name: "single key-value", + input: "tag=v1.0", + want: map[string]string{"tag": "v1.0"}, + }, + { + name: "multiple key-values", + input: "digest=sha256:abc123,tag=v1.0", + want: map[string]string{"digest": "sha256:abc123", "tag": "v1.0"}, + }, + { + name: "whitespace around pairs", + input: " tag=v1.0 , branch=main ", + want: map[string]string{"tag": "v1.0", "branch": "main"}, + }, + { + name: "equals sign in value", + input: "digest=sha256:abc=123", + want: map[string]string{"digest": "sha256:abc=123"}, + }, + { + name: "missing equals sign", + input: "tag", + wantErr: true, + }, + { + name: "empty key", + input: "=value", + wantErr: true, + }, + { + name: "empty value", + input: "tag=", + wantErr: true, + }, + { + name: "trailing comma", + input: "tag=v1.0,", + want: map[string]string{"tag": "v1.0"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parseRefSpec(tt.input) + if tt.wantErr { + if err == nil { + t.Fatalf("expected error for input %q, got nil", tt.input) + } + return + } + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(got) != len(tt.want) { + t.Fatalf("expected %d entries, got %d: %v", len(tt.want), len(got), got) + } + for k, v := range tt.want { + if got[k] != v { + t.Errorf("expected %q=%q, got %q=%q", k, v, k, got[k]) + } + } + }) + } +} + +func TestValidateOCIRef(t *testing.T) { + tests := []struct { + name string + refMap map[string]string + wantErr bool + }{ + { + name: "valid tag", + refMap: map[string]string{"tag": "v1.0"}, + }, + { + name: "valid digest", + refMap: map[string]string{"digest": "sha256:abc123def456"}, + }, + { + name: "valid semver", + refMap: map[string]string{"semver": ">=1.0.0"}, + }, + { + name: "multiple valid keys", + refMap: map[string]string{"tag": "v1.0", "digest": "sha256:abc"}, + }, + { + name: "empty map", + refMap: map[string]string{}, + }, + { + name: "invalid key", + refMap: map[string]string{"branch": "main"}, + wantErr: true, + }, + { + name: "invalid digest format", + refMap: map[string]string{"digest": "md5:abc"}, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateOCIRef(tt.refMap) + if tt.wantErr && err == nil { + t.Fatal("expected error, got nil") + } + if !tt.wantErr && err != nil { + t.Fatalf("unexpected error: %v", err) + } + }) + } +} + +func TestValidateGitRef(t *testing.T) { + tests := []struct { + name string + refMap map[string]string + wantErr bool + }{ + { + name: "valid branch", + refMap: map[string]string{"branch": "main"}, + }, + { + name: "valid commit", + refMap: map[string]string{"commit": "abc1234"}, + }, + { + name: "valid tag and branch", + refMap: map[string]string{"tag": "v1.0", "branch": "release"}, + }, + { + name: "empty map", + refMap: map[string]string{}, + }, + { + name: "invalid key", + refMap: map[string]string{"digest": "sha256:abc"}, + wantErr: true, + }, + { + name: "commit too short", + refMap: map[string]string{"commit": "abc"}, + wantErr: true, + }, + { + name: "commit not hex", + refMap: map[string]string{"commit": "zzzzzzz"}, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateGitRef(tt.refMap) + if tt.wantErr && err == nil { + t.Fatal("expected error, got nil") + } + if !tt.wantErr && err != nil { + t.Fatalf("unexpected error: %v", err) + } + }) + } +} + +func TestGenerateOCIRepository(t *testing.T) { + refMap := map[string]string{"tag": "v1.0", "digest": "sha256:abc123"} + obj, err := generateOCIRepository("my-repo", "oci://registry.example.com/repo", refMap) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if obj.Name != "my-repo" { + t.Errorf("expected name %q, got %q", "my-repo", obj.Name) + } + if obj.Namespace != "cozy-system" { + t.Errorf("expected namespace %q, got %q", "cozy-system", obj.Namespace) + } + if obj.Spec.URL != "oci://registry.example.com/repo" { + t.Errorf("expected URL %q, got %q", "oci://registry.example.com/repo", obj.Spec.URL) + } + if obj.Spec.Reference == nil { + t.Fatal("expected Reference to be set") + } + if obj.Spec.Reference.Tag != "v1.0" { + t.Errorf("expected tag %q, got %q", "v1.0", obj.Spec.Reference.Tag) + } + if obj.Spec.Reference.Digest != "sha256:abc123" { + t.Errorf("expected digest %q, got %q", "sha256:abc123", obj.Spec.Reference.Digest) + } +} + +func TestGenerateOCIRepository_NoRef(t *testing.T) { + obj, err := generateOCIRepository("my-repo", "oci://registry.example.com/repo", map[string]string{}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if obj.Spec.Reference != nil { + t.Error("expected Reference to be nil for empty refMap") + } +} + +func TestGenerateOCIRepository_InvalidRef(t *testing.T) { + _, err := generateOCIRepository("my-repo", "oci://registry.example.com/repo", map[string]string{"branch": "main"}) + if err == nil { + t.Fatal("expected error for invalid OCI ref key, got nil") + } +} + +func TestGenerateGitRepository(t *testing.T) { + refMap := map[string]string{"branch": "main", "commit": "abc1234def5678"} + obj, err := generateGitRepository("my-repo", "https://github.com/user/repo", refMap) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if obj.Name != "my-repo" { + t.Errorf("expected name %q, got %q", "my-repo", obj.Name) + } + if obj.Namespace != "cozy-system" { + t.Errorf("expected namespace %q, got %q", "cozy-system", obj.Namespace) + } + if obj.Spec.URL != "https://github.com/user/repo" { + t.Errorf("expected URL %q, got %q", "https://github.com/user/repo", obj.Spec.URL) + } + if obj.Spec.Reference == nil { + t.Fatal("expected Reference to be set") + } + if obj.Spec.Reference.Branch != "main" { + t.Errorf("expected branch %q, got %q", "main", obj.Spec.Reference.Branch) + } + if obj.Spec.Reference.Commit != "abc1234def5678" { + t.Errorf("expected commit %q, got %q", "abc1234def5678", obj.Spec.Reference.Commit) + } +} + +func TestGenerateGitRepository_NoRef(t *testing.T) { + obj, err := generateGitRepository("my-repo", "https://github.com/user/repo", map[string]string{}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if obj.Spec.Reference != nil { + t.Error("expected Reference to be nil for empty refMap") + } +} + +func TestGenerateGitRepository_InvalidRef(t *testing.T) { + _, err := generateGitRepository("my-repo", "https://github.com/user/repo", map[string]string{"digest": "sha256:abc"}) + if err == nil { + t.Fatal("expected error for invalid Git ref key, got nil") + } +} diff --git a/cmd/kubeovn-plunger/main.go b/cmd/kubeovn-plunger/main.go index 210405ce..611d030b 100644 --- a/cmd/kubeovn-plunger/main.go +++ b/cmd/kubeovn-plunger/main.go @@ -29,6 +29,7 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/metrics" @@ -131,6 +132,11 @@ func main() { HealthProbeBindAddress: probeAddr, LeaderElection: enableLeaderElection, LeaderElectionID: "29a0338b.cozystack.io", + Cache: cache.Options{ + DefaultNamespaces: map[string]cache.Config{ + kubeOVNNamespace: {}, + }, + }, // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily // when the Manager ends. This requires the binary to immediately end when the // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly diff --git a/dashboards/gpu/gpu-efficiency.json b/dashboards/gpu/gpu-efficiency.json new file mode 100644 index 00000000..28a24568 --- /dev/null +++ b/dashboards/gpu/gpu-efficiency.json @@ -0,0 +1,819 @@ +{ + "uid": "gpu-efficiency", + "title": "GPU Efficiency Score", + "description": "Tensor saturation, util/watt and throttling — reveals inefficient GPU workloads", + "tags": [ + "gpu", + "efficiency", + "finops" + ], + "timezone": "browser", + "editable": true, + "graphTooltip": 1, + "time": { + "from": "now-1h", + "to": "now" + }, + "fiscalYearStartMonth": 0, + "schemaVersion": 42, + "panels": [ + { + "type": "row", + "collapsed": false, + "title": "Overall efficiency metrics", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 1, + "panels": [] + }, + { + "type": "stat", + "id": 2, + "targets": [ + { + "expr": "avg(gpu:tensor_saturation:avg5m) * 100", + "refId": "A" + } + ], + "title": "Avg Tensor Saturation", + "description": "Mean tensor core saturation across all GPUs. \u003c10% means GPUs are used inefficiently (workloads could move to CPU or optimize their code). Cluster-wide — DCGM metrics cannot be attributed to workload namespaces.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 0, + "y": 1 + }, + "repeatDirection": "h", + "options": { + "graphMode": "area", + "colorMode": "background", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "percent", + "min": 0, + "max": 100, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "red" + }, + { + "value": 10, + "color": "orange" + }, + { + "value": 30, + "color": "yellow" + }, + { + "value": 60, + "color": "green" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "stat", + "id": 3, + "targets": [ + { + "expr": "avg(gpu:util_per_watt:avg5m)", + "refId": "A" + } + ], + "title": "Avg Utilization per Watt", + "description": "NVML utilization % per watt across all GPUs. Higher value = more efficient workload. Cluster-wide — DCGM metrics cannot be attributed to workload namespaces.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 8, + "y": 1 + }, + "repeatDirection": "h", + "options": { + "graphMode": "area", + "colorMode": "background", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "none", + "decimals": 3, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "red" + }, + { + "value": 0.5, + "color": "yellow" + }, + { + "value": 1, + "color": "green" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "stat", + "id": 4, + "targets": [ + { + "expr": "avg(gpu:power_throttle_fraction:rate5m)", + "refId": "A" + } + ], + "title": "Avg Power Throttling", + "description": "Fraction of time GPUs hit the TDP cap and lose performance. \u003e5% means tenants underutilize billed FLOPS. Cluster-wide — rule aggregates by (Hostname, gpu, UUID) and drops namespace label.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 16, + "y": 1 + }, + "repeatDirection": "h", + "options": { + "graphMode": "area", + "colorMode": "background", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "percentunit", + "decimals": 2, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "green" + }, + { + "value": 0.05, + "color": "yellow" + }, + { + "value": 0.2, + "color": "red" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "row", + "collapsed": false, + "title": "NVML vs Tensor (mismatch detector)", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 6 + }, + "id": 10, + "panels": [] + }, + { + "type": "timeseries", + "id": 11, + "targets": [ + { + "expr": "DCGM_FI_DEV_GPU_UTIL", + "legendFormat": "{{Hostname}}/{{gpu}}", + "refId": "A" + } + ], + "title": "NVML GPU Utilization", + "description": "Classic utilization metric. Shows activity of any engine (SM, copy, encoder). Cluster-wide — DCGM namespace is the exporter's own namespace, not the workload namespace.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 7 + }, + "repeatDirection": "h", + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "showLegend": false, + "calcs": [ + "mean", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "" + } + }, + "fieldConfig": { + "defaults": { + "unit": "percent", + "min": 0, + "max": 100, + "custom": { + "drawStyle": "line", + "lineInterpolation": "linear", + "fillOpacity": 10, + "showPoints": "never" + } + }, + "overrides": [] + } + }, + { + "type": "timeseries", + "id": 12, + "targets": [ + { + "expr": "DCGM_FI_PROF_PIPE_TENSOR_ACTIVE * 100", + "legendFormat": "{{Hostname}}/{{gpu}}", + "refId": "A" + } + ], + "title": "Tensor Pipe Active", + "description": "Real tensor core load (HMMA). For AI/LLM inference it should be ≥20%, otherwise the workload is unoptimized. Cluster-wide — DCGM namespace is the exporter's own namespace, not the workload namespace.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 7 + }, + "repeatDirection": "h", + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "showLegend": false, + "calcs": [ + "mean", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "" + } + }, + "fieldConfig": { + "defaults": { + "unit": "percent", + "min": 0, + "max": 100, + "custom": { + "drawStyle": "line", + "lineInterpolation": "linear", + "fillOpacity": 10, + "showPoints": "never" + } + }, + "overrides": [] + } + }, + { + "type": "row", + "collapsed": false, + "title": "Per-GPU ranking", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 15 + }, + "id": 20, + "panels": [] + }, + { + "type": "table", + "id": 21, + "targets": [ + { + "expr": "topk(20, gpu:tensor_saturation:avg5m * 100)", + "instant": true, + "range": false, + "format": "table", + "refId": "A" + } + ], + "title": "Tensor Saturation per GPU (5m avg)", + "description": "Which GPUs are exercising tensor cores and which are not. Sorted descending. Grouped by Hostname/gpu/UUID — DCGM metrics cannot be attributed to workload namespaces.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "repeatDirection": "h", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "DCGM_FI_DRIVER_VERSION": true, + "Time": true, + "__name__": true, + "cluster": true, + "container": true, + "device": true, + "endpoint": true, + "gpu_driver_version": true, + "instance": true, + "job": true, + "modelName": true, + "namespace": true, + "pci_bus_id": true, + "pod": true, + "prometheus": true, + "service": true, + "tenant": true, + "tier": true, + "uid": true, + "unit": true + }, + "indexByName": { + "Hostname": 0, + "UUID": 2, + "Value": 3, + "gpu": 1 + }, + "renameByName": { + "Hostname": "Node", + "UUID": "UUID", + "Value": "Saturation", + "gpu": "GPU" + } + } + } + ], + "options": { + "frameIndex": 0, + "showHeader": true, + "showTypeIcons": false, + "sortBy": [ + { + "displayName": "Saturation", + "desc": true + } + ], + "footer": { + "show": false, + "reducer": [] + }, + "cellHeight": "sm" + }, + "fieldConfig": { + "defaults": {}, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Saturation" + }, + "properties": [ + { + "id": "unit", + "value": "percent" + }, + { + "id": "min", + "value": 0 + }, + { + "id": "max", + "value": 100 + }, + { + "id": "custom.cellOptions", + "value": { + "mode": "gradient", + "type": "gauge" + } + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "red" + }, + { + "color": "orange", + "value": 10 + }, + { + "color": "yellow", + "value": 30 + }, + { + "color": "green", + "value": 60 + } + ] + } + } + ] + } + ] + } + }, + { + "type": "table", + "id": 22, + "targets": [ + { + "expr": "topk(20, gpu:util_per_watt:avg5m)", + "instant": true, + "range": false, + "format": "table", + "refId": "A" + } + ], + "title": "Utilization / Watt per GPU (5m avg)", + "description": "How efficiently each GPU spends watts. Low value = poor optimization. Grouped by Hostname/gpu/UUID — DCGM metrics cannot be attributed to workload namespaces.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + }, + "repeatDirection": "h", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "DCGM_FI_DRIVER_VERSION": true, + "Time": true, + "__name__": true, + "cluster": true, + "container": true, + "device": true, + "endpoint": true, + "gpu_driver_version": true, + "instance": true, + "job": true, + "modelName": true, + "namespace": true, + "pci_bus_id": true, + "pod": true, + "prometheus": true, + "service": true, + "tenant": true, + "tier": true, + "uid": true, + "unit": true + }, + "indexByName": { + "Hostname": 0, + "UUID": 2, + "Value": 3, + "gpu": 1 + }, + "renameByName": { + "Hostname": "Node", + "UUID": "UUID", + "Value": "Util/Watt", + "gpu": "GPU" + } + } + } + ], + "options": { + "frameIndex": 0, + "showHeader": true, + "showTypeIcons": false, + "sortBy": [ + { + "displayName": "Util/Watt", + "desc": true + } + ], + "footer": { + "show": false, + "reducer": [] + }, + "cellHeight": "sm" + }, + "fieldConfig": { + "defaults": {}, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Util/Watt" + }, + "properties": [ + { + "id": "unit", + "value": "none" + }, + { + "id": "decimals", + "value": 3 + }, + { + "id": "min", + "value": 0 + }, + { + "id": "max", + "value": 1.5 + }, + { + "id": "custom.cellOptions", + "value": { + "mode": "gradient", + "type": "gauge" + } + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "red" + }, + { + "color": "yellow", + "value": 0.5 + }, + { + "color": "green", + "value": 1 + } + ] + } + } + ] + } + ] + } + }, + { + "type": "row", + "collapsed": false, + "title": "Throttling", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 24 + }, + "id": 30, + "panels": [] + }, + { + "type": "timeseries", + "id": 31, + "targets": [ + { + "expr": "gpu:power_throttle_fraction:rate5m", + "legendFormat": "{{Hostname}}/{{gpu}}", + "refId": "A" + } + ], + "title": "Power throttle fraction per GPU", + "description": "Fraction of time the GPU was power-throttled. 1.0 = always throttled. Cluster-wide — rule aggregates by (Hostname, gpu, UUID) and drops namespace label.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 25 + }, + "repeatDirection": "h", + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "showLegend": false, + "calcs": [ + "mean", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "" + } + }, + "fieldConfig": { + "defaults": { + "unit": "percentunit", + "min": 0, + "max": 1, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "green" + }, + { + "value": 0.05, + "color": "yellow" + }, + { + "value": 0.2, + "color": "red" + } + ] + }, + "custom": { + "drawStyle": "line", + "lineInterpolation": "linear", + "fillOpacity": 25, + "showPoints": "never" + } + }, + "overrides": [] + } + }, + { + "type": "timeseries", + "id": 32, + "targets": [ + { + "expr": "gpu:thermal_throttle_fraction:rate5m", + "legendFormat": "{{Hostname}}/{{gpu}}", + "refId": "A" + } + ], + "title": "Thermal throttle fraction per GPU", + "description": "Fraction of time the GPU was thermal-throttled. Cluster-wide — rule aggregates by (Hostname, gpu, UUID) and drops namespace label.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 25 + }, + "repeatDirection": "h", + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "showLegend": false, + "calcs": [ + "mean", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "" + } + }, + "fieldConfig": { + "defaults": { + "unit": "percentunit", + "min": 0, + "max": 1, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "green" + }, + { + "value": 0.05, + "color": "yellow" + }, + { + "value": 0.2, + "color": "red" + } + ] + }, + "custom": { + "drawStyle": "line", + "lineInterpolation": "linear", + "fillOpacity": 25, + "showPoints": "never" + } + }, + "overrides": [] + } + } + ], + "templating": { + "list": [ + { + "type": "datasource", + "name": "ds_prometheus", + "label": "Prometheus", + "skipUrlSync": false, + "query": "prometheus", + "current": { + "selected": false, + "text": "default", + "value": "default" + }, + "multi": false, + "allowCustomValue": true, + "includeAll": false, + "regex": "", + "auto": false, + "auto_min": "10s", + "auto_count": 30 + } + ] + }, + "annotations": {} +} diff --git a/dashboards/gpu/gpu-fleet.json b/dashboards/gpu/gpu-fleet.json new file mode 100644 index 00000000..60c12bfb --- /dev/null +++ b/dashboards/gpu/gpu-fleet.json @@ -0,0 +1,1157 @@ +{ + "uid": "gpu-fleet", + "title": "GPU Fleet Overview", + "description": "Cluster-wide GPU inventory, capacity, utilization and health — admin view across the whole fleet.", + "tags": [ + "gpu", + "fleet", + "admin" + ], + "timezone": "browser", + "editable": true, + "graphTooltip": 1, + "time": { + "from": "now-6h", + "to": "now" + }, + "fiscalYearStartMonth": 0, + "schemaVersion": 42, + "panels": [ + { + "type": "row", + "collapsed": false, + "title": "Cluster snapshot", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 1, + "panels": [] + }, + { + "type": "stat", + "id": 2, + "targets": [ + { + "expr": "cluster:gpu_count:total", + "refId": "A" + } + ], + "title": "Total GPUs", + "description": "Physical GPUs discovered by DCGM across all nodes.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 0, + "y": 1 + }, + "repeatDirection": "h", + "options": { + "graphMode": "none", + "colorMode": "value", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "blue" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "stat", + "id": 3, + "targets": [ + { + "expr": "cluster:gpu_count:allocated", + "refId": "A" + } + ], + "title": "Allocated", + "description": "Sum of kube GPU requests across all pods. Billable view — includes Pending pods.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 4, + "y": 1 + }, + "repeatDirection": "h", + "options": { + "graphMode": "none", + "colorMode": "background", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "blue" + }, + { + "value": 1, + "color": "green" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "stat", + "id": 4, + "targets": [ + { + "expr": "cluster:gpu_count:free", + "refId": "A" + } + ], + "title": "Free", + "description": "Unallocated capacity available for new tenants.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 8, + "y": 1 + }, + "repeatDirection": "h", + "options": { + "graphMode": "none", + "colorMode": "background", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "red" + }, + { + "value": 1, + "color": "yellow" + }, + { + "value": 2, + "color": "green" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "stat", + "id": 5, + "targets": [ + { + "expr": "count(count by (Hostname) (DCGM_FI_DEV_GPU_UTIL))", + "refId": "A" + } + ], + "title": "Nodes with GPU", + "description": "Nodes reporting at least one GPU to DCGM. Counted via DCGM_FI_DEV_GPU_UTIL because kube-state-metrics does not expose node labels by default.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 12, + "y": 1 + }, + "repeatDirection": "h", + "options": { + "graphMode": "none", + "colorMode": "value", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "blue" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "stat", + "id": 6, + "targets": [ + { + "expr": "cluster:gpu_util:avg", + "refId": "A" + } + ], + "title": "Average utilization", + "description": "Legacy NVML utilization across all tenant GPUs.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 16, + "y": 1 + }, + "repeatDirection": "h", + "options": { + "graphMode": "area", + "colorMode": "value", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "percent", + "min": 0, + "max": 100, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "blue" + }, + { + "value": 30, + "color": "green" + }, + { + "value": 80, + "color": "orange" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "stat", + "id": 7, + "targets": [ + { + "expr": "cluster:gpu_power_watts:sum", + "refId": "A" + } + ], + "title": "Total power", + "description": "Live power draw summed across the fleet.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 20, + "y": 1 + }, + "repeatDirection": "h", + "options": { + "graphMode": "area", + "colorMode": "value", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "watt", + "decimals": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "green" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "row", + "collapsed": false, + "title": "Per-node GPU activity", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 5 + }, + "id": 10, + "panels": [] + }, + { + "type": "bargauge", + "id": 11, + "targets": [ + { + "expr": "avg by (Hostname) (DCGM_FI_DEV_GPU_UTIL)", + "legendFormat": "{{Hostname}}", + "refId": "A" + } + ], + "title": "GPU utilization per node", + "description": "Average NVML utilization per node — spot idle or saturated hosts at a glance.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 6 + }, + "repeatDirection": "h", + "options": { + "displayMode": "gradient", + "valueMode": "color", + "namePlacement": "auto", + "showUnfilled": true, + "sizing": "auto", + "minVizWidth": 8, + "minVizHeight": 16, + "legend": { + "displayMode": "list", + "placement": "bottom", + "showLegend": false, + "calcs": [] + }, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "maxVizHeight": 300, + "orientation": "horizontal" + }, + "fieldConfig": { + "defaults": { + "unit": "percent", + "min": 0, + "max": 100, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "blue" + }, + { + "value": 30, + "color": "green" + }, + { + "value": 80, + "color": "orange" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "bargauge", + "id": 12, + "targets": [ + { + "expr": "sum by (Hostname) (DCGM_FI_DEV_POWER_USAGE) / clamp_min(sum by (Hostname) (DCGM_FI_DEV_POWER_MGMT_LIMIT), 1) * 100", + "legendFormat": "{{Hostname}}", + "refId": "A" + } + ], + "title": "Power draw per node (% of TDP)", + "description": "GPU power usage as a fraction of the node's combined TDP cap. Works across GPU SKUs without per-model thresholds. Requires DCGM_FI_DEV_POWER_MGMT_LIMIT from custom DCGM CSV (see packages/system/gpu-operator/examples/dcgm-custom-metrics.yaml).", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 6 + }, + "repeatDirection": "h", + "options": { + "displayMode": "gradient", + "valueMode": "color", + "namePlacement": "auto", + "showUnfilled": true, + "sizing": "auto", + "minVizWidth": 8, + "minVizHeight": 16, + "legend": { + "displayMode": "list", + "placement": "bottom", + "showLegend": false, + "calcs": [] + }, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "maxVizHeight": 300, + "orientation": "horizontal" + }, + "fieldConfig": { + "defaults": { + "unit": "percent", + "min": 0, + "max": 100, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "green" + }, + { + "value": 60, + "color": "yellow" + }, + { + "value": 80, + "color": "orange" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "row", + "collapsed": false, + "title": "Usage trends", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 14 + }, + "id": 20, + "panels": [] + }, + { + "type": "timeseries", + "id": 21, + "targets": [ + { + "expr": "avg by (Hostname) (DCGM_FI_DEV_GPU_UTIL)", + "legendFormat": "{{Hostname}}", + "refId": "A" + } + ], + "title": "GPU utilization per node (trend)", + "description": "Per-node NVML utilization over time — shows shifting workload across hosts.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 15 + }, + "repeatDirection": "h", + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "showLegend": false, + "calcs": [ + "mean", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "" + } + }, + "fieldConfig": { + "defaults": { + "unit": "percent", + "min": 0, + "max": 100, + "custom": { + "drawStyle": "line", + "lineInterpolation": "linear", + "fillOpacity": 20, + "showPoints": "never" + } + }, + "overrides": [] + } + }, + { + "type": "timeseries", + "id": 22, + "targets": [ + { + "expr": "sum by (Hostname) (DCGM_FI_DEV_POWER_USAGE)", + "legendFormat": "{{Hostname}}", + "refId": "A" + } + ], + "title": "Power draw per node (trend)", + "description": "Per-node GPU power consumption over time.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 15 + }, + "repeatDirection": "h", + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "showLegend": false, + "calcs": [ + "mean", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "" + } + }, + "fieldConfig": { + "defaults": { + "unit": "watt", + "custom": { + "drawStyle": "line", + "lineInterpolation": "linear", + "fillOpacity": 20, + "showPoints": "never" + } + }, + "overrides": [] + } + }, + { + "type": "row", + "collapsed": false, + "title": "Health", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 23 + }, + "id": 30, + "panels": [] + }, + { + "type": "stat", + "id": 31, + "targets": [ + { + "expr": "count((max by (Hostname, gpu) (DCGM_FI_DEV_XID_ERRORS)) \u003e 0) or vector(0)", + "refId": "A" + } + ], + "title": "GPUs with XID errors", + "description": "Count of GPUs reporting a non-zero XID code. XID = NVIDIA hardware/driver error — any non-zero value needs investigation.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 24 + }, + "repeatDirection": "h", + "options": { + "graphMode": "none", + "colorMode": "background", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "green" + }, + { + "value": 1, + "color": "red" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "stat", + "id": 32, + "targets": [ + { + "expr": "avg(gpu:power_throttle_fraction:rate5m)", + "refId": "A" + } + ], + "title": "Power throttling (cluster avg)", + "description": "Fraction of time the fleet spent power-throttled. \u003e5% = billed FLOPS are being underdelivered.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 24 + }, + "repeatDirection": "h", + "options": { + "graphMode": "none", + "colorMode": "background", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "percentunit", + "decimals": 2, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "green" + }, + { + "value": 0.05, + "color": "yellow" + }, + { + "value": 0.2, + "color": "red" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "stat", + "id": 33, + "targets": [ + { + "expr": "avg(gpu:thermal_throttle_fraction:rate5m)", + "refId": "A" + } + ], + "title": "Thermal throttling (cluster avg)", + "description": "Fraction of time the fleet spent thermal-throttled. Indicates cooling/datacenter issues.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 24 + }, + "repeatDirection": "h", + "options": { + "graphMode": "none", + "colorMode": "background", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "percentunit", + "decimals": 2, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "green" + }, + { + "value": 0.05, + "color": "yellow" + }, + { + "value": 0.2, + "color": "red" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "stat", + "id": 34, + "targets": [ + { + "expr": "max(DCGM_FI_DEV_GPU_TEMP)", + "refId": "A" + } + ], + "title": "Max GPU temperature", + "description": "Hottest GPU in the fleet right now. Adjust thresholds to match your GPU model specifications.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 24 + }, + "repeatDirection": "h", + "options": { + "graphMode": "none", + "colorMode": "background", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "celsius", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "green" + }, + { + "value": 75, + "color": "yellow" + }, + { + "value": 85, + "color": "red" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "timeseries", + "id": 35, + "targets": [ + { + "expr": "DCGM_FI_DEV_GPU_TEMP", + "legendFormat": "{{Hostname}}/{{gpu}}", + "refId": "A" + } + ], + "title": "Temperature per GPU", + "description": "Per-GPU temperature trace. Sustained red zone = cooling issue or aggressive workload.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 29 + }, + "repeatDirection": "h", + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "showLegend": false, + "calcs": [ + "mean", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "" + } + }, + "fieldConfig": { + "defaults": { + "unit": "celsius", + "min": 20, + "max": 100, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "green" + }, + { + "value": 75, + "color": "yellow" + }, + { + "value": 85, + "color": "red" + } + ] + }, + "custom": { + "drawStyle": "line", + "lineInterpolation": "linear", + "fillOpacity": 10, + "showPoints": "never" + } + }, + "overrides": [] + } + }, + { + "type": "timeseries", + "id": 36, + "targets": [ + { + "expr": "gpu:power_throttle_fraction:rate5m", + "legendFormat": "power {{Hostname}}/{{gpu}}", + "refId": "A" + }, + { + "expr": "gpu:thermal_throttle_fraction:rate5m", + "legendFormat": "thermal {{Hostname}}/{{gpu}}", + "refId": "B" + } + ], + "title": "Throttle fraction per GPU", + "description": "Red = power throttle, blue = thermal throttle. Both on same chart to compare causes.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 29 + }, + "repeatDirection": "h", + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "showLegend": false, + "calcs": [ + "mean", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "" + } + }, + "fieldConfig": { + "defaults": { + "unit": "percentunit", + "min": 0, + "max": 1, + "custom": { + "drawStyle": "line", + "lineInterpolation": "linear", + "fillOpacity": 25, + "showPoints": "never" + } + }, + "overrides": [] + } + }, + { + "type": "row", + "collapsed": false, + "title": "Hardware inventory", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 37 + }, + "id": 40, + "panels": [] + }, + { + "type": "table", + "id": 41, + "targets": [ + { + "expr": "group by (Hostname, modelName, gpu, UUID) (DCGM_FI_DEV_GPU_UTIL)", + "instant": true, + "range": false, + "format": "table", + "refId": "A" + } + ], + "title": "GPU inventory", + "description": "One row per physical GPU. Model / index / UUID sourced from DCGM labels.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 38 + }, + "repeatDirection": "h", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "Value": true, + "__name__": true, + "cluster": true, + "container": true, + "device": true, + "endpoint": true, + "instance": true, + "job": true, + "pci_bus_id": true, + "prometheus": true, + "service": true, + "tenant": true, + "tier": true, + "uid": true, + "unit": true + }, + "indexByName": { + "Hostname": 0, + "UUID": 3, + "gpu": 2, + "modelName": 1 + }, + "renameByName": { + "Hostname": "Node", + "UUID": "UUID", + "gpu": "Index", + "modelName": "GPU Model" + } + } + } + ], + "options": { + "frameIndex": 0, + "showHeader": true, + "showTypeIcons": false, + "footer": { + "show": false, + "reducer": [] + }, + "cellHeight": "sm" + } + } + ], + "templating": { + "list": [ + { + "type": "datasource", + "name": "ds_prometheus", + "label": "Prometheus", + "skipUrlSync": false, + "query": "prometheus", + "current": { + "selected": false, + "text": "default", + "value": "default" + }, + "multi": false, + "allowCustomValue": true, + "includeAll": false, + "regex": "", + "auto": false, + "auto_min": "10s", + "auto_count": 30 + } + ] + }, + "annotations": {} +} diff --git a/dashboards/gpu/gpu-performance.json b/dashboards/gpu/gpu-performance.json new file mode 100644 index 00000000..aed9c69a --- /dev/null +++ b/dashboards/gpu/gpu-performance.json @@ -0,0 +1,957 @@ +{ + "uid": "gpu-performance", + "title": "GPU Performance", + "tags": [ + "gpu", + "dcgm" + ], + "timezone": "browser", + "editable": true, + "graphTooltip": 1, + "time": { + "from": "now-1h", + "to": "now" + }, + "fiscalYearStartMonth": 0, + "schemaVersion": 42, + "panels": [ + { + "type": "row", + "collapsed": false, + "title": "Overview", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 1, + "panels": [] + }, + { + "type": "stat", + "id": 2, + "targets": [ + { + "expr": "cluster:gpu_count:total", + "refId": "A" + } + ], + "title": "Total GPUs", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 0, + "y": 1 + }, + "repeatDirection": "h", + "options": { + "graphMode": "none", + "colorMode": "value", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "blue" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "stat", + "id": 3, + "targets": [ + { + "expr": "cluster:gpu_count:allocated", + "refId": "A" + } + ], + "title": "Allocated", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 6, + "y": 1 + }, + "repeatDirection": "h", + "options": { + "graphMode": "none", + "colorMode": "value", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "green" + }, + { + "value": 1, + "color": "yellow" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "stat", + "id": 4, + "targets": [ + { + "expr": "cluster:gpu_util:avg", + "refId": "A" + } + ], + "title": "Average utilization", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 12, + "y": 1 + }, + "repeatDirection": "h", + "options": { + "graphMode": "area", + "colorMode": "value", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "percent", + "min": 0, + "max": 100, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "blue" + }, + { + "value": 30, + "color": "green" + }, + { + "value": 80, + "color": "orange" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "stat", + "id": 5, + "targets": [ + { + "expr": "cluster:gpu_power_watts:sum", + "refId": "A" + } + ], + "title": "Power draw", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 18, + "y": 1 + }, + "repeatDirection": "h", + "options": { + "graphMode": "area", + "colorMode": "value", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "watt", + "decimals": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "green" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "row", + "collapsed": false, + "title": "Utilization", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 5 + }, + "id": 10, + "panels": [] + }, + { + "type": "timeseries", + "id": 11, + "targets": [ + { + "expr": "DCGM_FI_DEV_GPU_UTIL{Hostname=~\"$Hostname\"}", + "legendFormat": "{{Hostname}}/{{gpu}} {{pod}}", + "refId": "A" + } + ], + "title": "GPU Utilization (NVML)", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 6 + }, + "repeatDirection": "h", + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "showLegend": false, + "calcs": [ + "mean", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "" + } + }, + "fieldConfig": { + "defaults": { + "unit": "percent", + "min": 0, + "max": 100, + "custom": { + "drawStyle": "line", + "lineInterpolation": "linear", + "fillOpacity": 10, + "showPoints": "never", + "spanNulls": false + } + }, + "overrides": [] + } + }, + { + "type": "timeseries", + "id": 12, + "targets": [ + { + "expr": "DCGM_FI_PROF_PIPE_TENSOR_ACTIVE{Hostname=~\"$Hostname\"} * 100", + "legendFormat": "{{Hostname}}/{{gpu}} {{pod}}", + "refId": "A" + } + ], + "title": "Tensor Pipe Active (realistic load for LLM/AI)", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 6 + }, + "repeatDirection": "h", + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "showLegend": false, + "calcs": [ + "mean", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "" + } + }, + "fieldConfig": { + "defaults": { + "unit": "percent", + "min": 0, + "max": 100, + "custom": { + "drawStyle": "line", + "lineInterpolation": "linear", + "fillOpacity": 10, + "showPoints": "never", + "spanNulls": false + } + }, + "overrides": [] + } + }, + { + "type": "timeseries", + "id": 13, + "targets": [ + { + "expr": "DCGM_FI_PROF_GR_ENGINE_ACTIVE{Hostname=~\"$Hostname\"} * 100", + "legendFormat": "{{Hostname}}/{{gpu}} {{pod}}", + "refId": "A" + } + ], + "title": "Graphics Engine Active", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 14 + }, + "repeatDirection": "h", + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "showLegend": false, + "calcs": [ + "mean", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "" + } + }, + "fieldConfig": { + "defaults": { + "unit": "percent", + "min": 0, + "max": 100, + "custom": { + "drawStyle": "line", + "lineInterpolation": "linear", + "fillOpacity": 10, + "showPoints": "never", + "spanNulls": false + } + }, + "overrides": [] + } + }, + { + "type": "timeseries", + "id": 14, + "targets": [ + { + "expr": "DCGM_FI_DEV_MEM_COPY_UTIL{Hostname=~\"$Hostname\"}", + "legendFormat": "{{Hostname}}/{{gpu}} {{pod}}", + "refId": "A" + } + ], + "title": "Memory Copy Utilization", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 14 + }, + "repeatDirection": "h", + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "showLegend": false, + "calcs": [ + "mean", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "" + } + }, + "fieldConfig": { + "defaults": { + "unit": "percent", + "min": 0, + "max": 100, + "custom": { + "drawStyle": "line", + "lineInterpolation": "linear", + "fillOpacity": 10, + "showPoints": "never", + "spanNulls": false + } + }, + "overrides": [] + } + }, + { + "type": "row", + "collapsed": false, + "title": "Memory", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 22 + }, + "id": 20, + "panels": [] + }, + { + "type": "timeseries", + "id": 21, + "targets": [ + { + "expr": "DCGM_FI_DEV_FB_USED{Hostname=~\"$Hostname\"} * 1048576", + "legendFormat": "{{Hostname}}/{{gpu}} {{pod}}", + "refId": "A" + } + ], + "title": "VRAM Used", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 23 + }, + "repeatDirection": "h", + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "showLegend": false, + "calcs": [ + "mean", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "" + } + }, + "fieldConfig": { + "defaults": { + "unit": "bytes", + "custom": { + "drawStyle": "line", + "lineInterpolation": "linear", + "fillOpacity": 25, + "showPoints": "never" + } + }, + "overrides": [] + } + }, + { + "type": "timeseries", + "id": 22, + "targets": [ + { + "expr": "DCGM_FI_DEV_FB_FREE{Hostname=~\"$Hostname\"} * 1048576", + "legendFormat": "{{Hostname}}/{{gpu}}", + "refId": "A" + } + ], + "title": "VRAM Free", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 23 + }, + "repeatDirection": "h", + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "showLegend": false, + "calcs": [ + "mean", + "min" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "" + } + }, + "fieldConfig": { + "defaults": { + "unit": "bytes", + "custom": { + "drawStyle": "line", + "lineInterpolation": "linear", + "fillOpacity": 10, + "showPoints": "never" + } + }, + "overrides": [] + } + }, + { + "type": "row", + "collapsed": false, + "title": "Power \u0026 Temperature", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 31 + }, + "id": 30, + "panels": [] + }, + { + "type": "timeseries", + "id": 31, + "targets": [ + { + "expr": "DCGM_FI_DEV_POWER_USAGE{Hostname=~\"$Hostname\"}", + "legendFormat": "{{Hostname}}/{{gpu}}", + "refId": "A" + } + ], + "title": "Power Usage per GPU", + "description": "Hardware-level metric — shown cluster-wide regardless of namespace filter.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 32 + }, + "repeatDirection": "h", + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "showLegend": false, + "calcs": [ + "mean", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "" + } + }, + "fieldConfig": { + "defaults": { + "unit": "watt", + "custom": { + "drawStyle": "line", + "lineInterpolation": "linear", + "fillOpacity": 10, + "showPoints": "never" + } + }, + "overrides": [] + } + }, + { + "type": "timeseries", + "id": 32, + "targets": [ + { + "expr": "DCGM_FI_DEV_GPU_TEMP{Hostname=~\"$Hostname\"}", + "legendFormat": "{{Hostname}}/{{gpu}}", + "refId": "A" + } + ], + "title": "GPU Temperature", + "description": "Hardware-level metric — shown cluster-wide regardless of namespace filter.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 32 + }, + "repeatDirection": "h", + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "showLegend": false, + "calcs": [ + "mean", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "" + } + }, + "fieldConfig": { + "defaults": { + "unit": "celsius", + "min": 20, + "max": 100, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "green" + }, + { + "value": 75, + "color": "yellow" + }, + { + "value": 85, + "color": "red" + } + ] + }, + "custom": { + "drawStyle": "line", + "lineInterpolation": "linear", + "fillOpacity": 10, + "showPoints": "never" + } + }, + "overrides": [] + } + }, + { + "type": "row", + "collapsed": false, + "title": "Health", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 40 + }, + "id": 40, + "panels": [] + }, + { + "type": "stat", + "id": 41, + "targets": [ + { + "expr": "max by (Hostname, gpu) (DCGM_FI_DEV_XID_ERRORS{Hostname=~\"$Hostname\"})", + "legendFormat": "{{Hostname}}/{{gpu}}", + "refId": "A" + } + ], + "title": "XID errors (latest)", + "description": "Hardware-level metric — shown cluster-wide regardless of namespace filter.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 0, + "y": 41 + }, + "repeatDirection": "h", + "options": { + "graphMode": "none", + "colorMode": "background", + "justifyMode": "auto", + "textMode": "value_and_name", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "green" + }, + { + "value": 1, + "color": "red" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "timeseries", + "id": 42, + "targets": [ + { + "expr": "rate(DCGM_FI_DEV_POWER_VIOLATION{Hostname=~\"$Hostname\"}[5m])", + "legendFormat": "{{Hostname}}/{{gpu}}", + "refId": "A" + } + ], + "title": "Power Violation (µs/s throttled due to power)", + "description": "Hardware-level metric — shown cluster-wide regardless of namespace filter.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 8, + "y": 41 + }, + "repeatDirection": "h", + "options": { + "legend": { + "displayMode": "list", + "placement": "bottom", + "showLegend": false, + "calcs": [] + }, + "tooltip": { + "mode": "multi", + "sort": "" + } + }, + "fieldConfig": { + "defaults": { + "unit": "µs", + "custom": { + "drawStyle": "line", + "lineInterpolation": "linear", + "fillOpacity": 10, + "showPoints": "never" + } + }, + "overrides": [] + } + }, + { + "type": "timeseries", + "id": 43, + "targets": [ + { + "expr": "rate(DCGM_FI_DEV_THERMAL_VIOLATION{Hostname=~\"$Hostname\"}[5m])", + "legendFormat": "{{Hostname}}/{{gpu}}", + "refId": "A" + } + ], + "title": "Thermal Violation (µs/s throttled due to thermals)", + "description": "Hardware-level metric — shown cluster-wide regardless of namespace filter.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 16, + "y": 41 + }, + "repeatDirection": "h", + "options": { + "legend": { + "displayMode": "list", + "placement": "bottom", + "showLegend": false, + "calcs": [] + }, + "tooltip": { + "mode": "multi", + "sort": "" + } + }, + "fieldConfig": { + "defaults": { + "unit": "µs", + "custom": { + "drawStyle": "line", + "lineInterpolation": "linear", + "fillOpacity": 10, + "showPoints": "never" + } + }, + "overrides": [] + } + } + ], + "templating": { + "list": [ + { + "type": "datasource", + "name": "ds_prometheus", + "label": "Prometheus", + "skipUrlSync": false, + "query": "prometheus", + "current": { + "selected": false, + "text": "default", + "value": "default" + }, + "multi": false, + "allowCustomValue": true, + "includeAll": false, + "regex": "", + "auto": false, + "auto_min": "10s", + "auto_count": 30 + }, + { + "type": "query", + "name": "Hostname", + "label": "Host", + "skipUrlSync": false, + "query": "label_values(DCGM_FI_DEV_GPU_UTIL, Hostname)", + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "current": { + "selected": true, + "text": "All", + "value": "$__all" + }, + "multi": true, + "allowCustomValue": true, + "refresh": 2, + "sort": 1, + "includeAll": true, + "auto": false, + "auto_min": "10s", + "auto_count": 30 + } + ] + }, + "annotations": {} +} diff --git a/dashboards/gpu/gpu-quotas.json b/dashboards/gpu/gpu-quotas.json new file mode 100644 index 00000000..865fb95c --- /dev/null +++ b/dashboards/gpu/gpu-quotas.json @@ -0,0 +1,637 @@ +{ + "uid": "gpu-quotas", + "title": "GPU Quotas \u0026 Allocation", + "tags": [ + "gpu", + "quotas" + ], + "timezone": "browser", + "editable": true, + "graphTooltip": 1, + "time": { + "from": "now-6h", + "to": "now" + }, + "fiscalYearStartMonth": 0, + "schemaVersion": 42, + "panels": [ + { + "type": "row", + "collapsed": false, + "title": "Allocation overview", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 1, + "panels": [] + }, + { + "type": "stat", + "id": 2, + "targets": [ + { + "expr": "sum(kube_node_status_allocatable{resource=\"nvidia_com_gpu\"})", + "refId": "A" + } + ], + "title": "GPU allocatable", + "description": "Total GPU capacity the cluster can schedule to pods.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 0, + "y": 1 + }, + "repeatDirection": "h", + "options": { + "graphMode": "none", + "colorMode": "value", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "blue" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "stat", + "id": 3, + "targets": [ + { + "expr": "cluster:gpu_count:allocated", + "refId": "A" + } + ], + "title": "GPU requested", + "description": "Sum of GPU requests across all pods cluster-wide, including system namespaces.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 6, + "y": 1 + }, + "repeatDirection": "h", + "options": { + "graphMode": "none", + "colorMode": "value", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "green" + }, + { + "value": 1, + "color": "yellow" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "gauge", + "id": 4, + "targets": [ + { + "expr": "cluster:gpu_count:allocated / sum(kube_node_status_allocatable{resource=\"nvidia_com_gpu\"}) * 100", + "refId": "A" + } + ], + "title": "Allocation ratio", + "description": "Percentage of allocatable GPUs currently requested by pods.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 12, + "y": 1 + }, + "repeatDirection": "h", + "options": { + "showThresholdLabels": false, + "showThresholdMarkers": true, + "sizing": "auto", + "minVizWidth": 75, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "minVizHeight": 75, + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "percent", + "min": 0, + "max": 100, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "blue" + }, + { + "value": 40, + "color": "green" + }, + { + "value": 80, + "color": "yellow" + }, + { + "value": 95, + "color": "red" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "stat", + "id": 5, + "targets": [ + { + "expr": "count((sum by (namespace, pod) (kube_pod_container_resource_requests{resource=\"nvidia_com_gpu\"}) \u003e 0) * on(namespace, pod) group_left() (kube_pod_status_phase{phase=\"Pending\"} == 1)) or vector(0)", + "refId": "A" + } + ], + "title": "Pending pods (GPU)", + "description": "Pods requesting GPUs that are stuck in Pending state — indicates capacity shortage.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 18, + "y": 1 + }, + "repeatDirection": "h", + "options": { + "graphMode": "none", + "colorMode": "background", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "green" + }, + { + "value": 1, + "color": "red" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "row", + "collapsed": false, + "title": "Per namespace", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 5 + }, + "id": 10, + "panels": [] + }, + { + "type": "bargauge", + "id": 11, + "targets": [ + { + "expr": "sum by (namespace) (namespace:gpu_count:allocated{namespace=~\"$namespace\"})", + "legendFormat": "{{namespace}}", + "refId": "A" + } + ], + "title": "GPU requested per namespace", + "description": "GPU allocation breakdown by namespace — spot top consumers at a glance.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 6 + }, + "repeatDirection": "h", + "options": { + "displayMode": "gradient", + "valueMode": "color", + "namePlacement": "auto", + "showUnfilled": true, + "sizing": "auto", + "minVizWidth": 8, + "minVizHeight": 16, + "legend": { + "displayMode": "list", + "placement": "bottom", + "showLegend": false, + "calcs": [] + }, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "maxVizHeight": 300, + "orientation": "horizontal" + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "min": 0, + "max": 8, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "green" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "timeseries", + "id": 12, + "targets": [ + { + "expr": "sum(kube_node_status_allocatable{resource=\"nvidia_com_gpu\"})", + "legendFormat": "Allocatable (total)", + "refId": "A" + }, + { + "expr": "sum(namespace:gpu_count:allocated{namespace=~\"$namespace\"})", + "legendFormat": "Requested", + "refId": "B" + } + ], + "title": "GPU allocated over time", + "description": "Requested vs allocatable GPUs over time — shows allocation pressure trends.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 6 + }, + "repeatDirection": "h", + "options": { + "legend": { + "displayMode": "list", + "placement": "bottom", + "showLegend": false, + "calcs": [] + }, + "tooltip": { + "mode": "multi", + "sort": "" + } + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "custom": { + "drawStyle": "line", + "lineInterpolation": "stepAfter", + "fillOpacity": 10, + "showPoints": "never" + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Allocatable (total)" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "blue", + "mode": "fixed" + } + } + ] + } + ] + } + }, + { + "type": "row", + "collapsed": false, + "title": "Pods with GPU", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 14 + }, + "id": 20, + "panels": [] + }, + { + "type": "table", + "id": 21, + "targets": [ + { + "expr": "kube_pod_container_resource_requests{resource=\"nvidia_com_gpu\", namespace=~\"$namespace\"} * on(namespace, pod) group_left(phase) (kube_pod_status_phase{phase=~\"Running|Pending\"} == 1)", + "instant": true, + "range": false, + "format": "table", + "refId": "requested" + }, + { + "expr": "kube_pod_container_resource_limits{resource=\"nvidia_com_gpu\", namespace=~\"$namespace\"} * on(namespace, pod) group_left() (kube_pod_status_phase{phase=~\"Running|Pending\"} == 1)", + "instant": true, + "range": false, + "format": "table", + "refId": "limits" + } + ], + "title": "Pods requesting GPU", + "description": "Per-pod GPU requests and limits with scheduling status — Running or Pending.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 15 + }, + "repeatDirection": "h", + "transformations": [ + { + "id": "joinByField", + "options": { + "byField": "pod", + "mode": "outer" + } + }, + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "Time 1": true, + "Time 2": true, + "__name__": true, + "__name__ 1": true, + "__name__ 2": true, + "cluster": true, + "cluster 1": true, + "cluster 2": true, + "container 2": true, + "endpoint": true, + "endpoint 1": true, + "endpoint 2": true, + "instance": true, + "instance 1": true, + "instance 2": true, + "job": true, + "job 1": true, + "job 2": true, + "namespace 2": true, + "node 2": true, + "prometheus": true, + "prometheus 1": true, + "prometheus 2": true, + "resource": true, + "resource 1": true, + "resource 2": true, + "service": true, + "service 1": true, + "service 2": true, + "tenant": true, + "tenant 1": true, + "tenant 2": true, + "tier": true, + "tier 1": true, + "tier 2": true, + "uid": true, + "uid 1": true, + "uid 2": true, + "unit": true, + "unit 1": true, + "unit 2": true + }, + "indexByName": { + "Value #limits": 5, + "Value #requested": 4, + "container 1": 2, + "namespace 1": 0, + "node 1": 3, + "phase": 6, + "pod": 1 + }, + "renameByName": { + "Value #limits": "Limit", + "Value #requested": "Req", + "container 1": "Container", + "namespace 1": "Namespace", + "node 1": "Node", + "phase": "Status" + } + } + } + ], + "options": { + "frameIndex": 0, + "showHeader": true, + "showTypeIcons": false, + "footer": { + "show": false, + "reducer": [] + }, + "cellHeight": "sm" + }, + "fieldConfig": { + "defaults": {}, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Status" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "basic", + "type": "color-background" + } + }, + { + "id": "mappings", + "value": [ + { + "options": { + "Failed": { + "color": "red", + "index": 2 + }, + "Pending": { + "color": "orange", + "index": 1 + }, + "Running": { + "color": "green", + "index": 0 + } + }, + "type": "value" + } + ] + } + ] + } + ] + } + } + ], + "templating": { + "list": [ + { + "type": "datasource", + "name": "ds_prometheus", + "label": "Prometheus", + "skipUrlSync": false, + "query": "prometheus", + "current": { + "selected": false, + "text": "default", + "value": "default" + }, + "multi": false, + "allowCustomValue": true, + "includeAll": false, + "regex": "", + "auto": false, + "auto_min": "10s", + "auto_count": 30 + }, + { + "type": "query", + "name": "namespace", + "label": "Namespace", + "skipUrlSync": false, + "query": "label_values(kube_pod_container_resource_requests{resource=\"nvidia_com_gpu\"}, namespace)", + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "current": { + "selected": true, + "text": "All", + "value": "$__all" + }, + "multi": true, + "allowCustomValue": true, + "refresh": 2, + "sort": 1, + "includeAll": true, + "auto": false, + "auto_min": "10s", + "auto_count": 30 + } + ] + }, + "annotations": {} +} diff --git a/dashboards/gpu/gpu-tenants.json b/dashboards/gpu/gpu-tenants.json new file mode 100644 index 00000000..7fef321d --- /dev/null +++ b/dashboards/gpu/gpu-tenants.json @@ -0,0 +1,1098 @@ +{ + "uid": "gpu-tenants", + "title": "GPU Tenant Usage", + "description": "Per-tenant GPU consumption: live allocation, utilization, tensor saturation, power, and 24h GPU-hours / kWh — admin view across all namespaces.", + "tags": [ + "gpu", + "tenants", + "admin" + ], + "timezone": "browser", + "editable": true, + "graphTooltip": 1, + "time": { + "from": "now-24h", + "to": "now" + }, + "fiscalYearStartMonth": 0, + "schemaVersion": 42, + "panels": [ + { + "type": "row", + "collapsed": false, + "title": "Tenant fleet snapshot", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 1, + "panels": [] + }, + { + "type": "stat", + "id": 2, + "targets": [ + { + "expr": "count(namespace:gpu_count:allocated{namespace=~\"$namespace\"} \u003e 0)", + "refId": "A" + } + ], + "title": "Active tenants", + "description": "Namespaces with at least one GPU request (Pending + Running).", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 0, + "y": 1 + }, + "repeatDirection": "h", + "options": { + "graphMode": "none", + "colorMode": "value", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "blue" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "stat", + "id": 3, + "targets": [ + { + "expr": "sum(namespace:gpu_count:allocated{namespace=~\"$namespace\"})", + "refId": "A" + } + ], + "title": "Allocated GPUs", + "description": "Sum of kube GPU requests across tenants. Billable view — includes Pending pods.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 4, + "y": 1 + }, + "repeatDirection": "h", + "options": { + "graphMode": "none", + "colorMode": "background", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "blue" + }, + { + "value": 1, + "color": "green" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "stat", + "id": 4, + "targets": [ + { + "expr": "cluster:gpu_util:avg", + "refId": "A" + } + ], + "title": "Avg cluster GPU util", + "description": "Average NVML utilization across all GPUs in the cluster. Cluster-wide — DCGM hardware metrics cannot be attributed to workload namespaces.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 8, + "y": 1 + }, + "repeatDirection": "h", + "options": { + "graphMode": "area", + "colorMode": "value", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "percent", + "min": 0, + "max": 100, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "blue" + }, + { + "value": 30, + "color": "green" + }, + { + "value": 80, + "color": "orange" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "stat", + "id": 5, + "targets": [ + { + "expr": "cluster:gpu_power_watts:sum", + "refId": "A" + } + ], + "title": "Total GPU power", + "description": "Live power draw summed across all GPUs in the cluster. Cluster-wide — DCGM hardware metrics cannot be attributed to workload namespaces.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 12, + "y": 1 + }, + "repeatDirection": "h", + "options": { + "graphMode": "area", + "colorMode": "value", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "watt", + "decimals": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "green" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "stat", + "id": 6, + "targets": [ + { + "expr": "sum(sum_over_time(node:power_watts:sum[24h:1m])) / 60 / 1000", + "refId": "A" + } + ], + "title": "Energy (last 24h)", + "description": "Integrated cluster-wide GPU power draw over 24h. Cluster-wide — DCGM hardware metrics cannot be attributed to workload namespaces.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 16, + "y": 1 + }, + "repeatDirection": "h", + "options": { + "graphMode": "none", + "colorMode": "value", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "kwatth", + "decimals": 1, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "green" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "stat", + "id": 7, + "targets": [ + { + "expr": "sum(sum_over_time(namespace:gpu_count:allocated{namespace=~\"$namespace\"}[24h:1m])) / 60", + "refId": "A" + } + ], + "title": "GPU-hours (last 24h)", + "description": "Sum of (GPU count × time requested) across all tenants over the last 24h. Integrated from kube requests, so Pending pods count.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 20, + "y": 1 + }, + "repeatDirection": "h", + "options": { + "graphMode": "none", + "colorMode": "value", + "justifyMode": "auto", + "textMode": "value", + "wideLayout": true, + "showPercentChange": false, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "percentChangeColorMode": "standard", + "orientation": "" + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "decimals": 1, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "blue" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "row", + "collapsed": false, + "title": "Current allocation", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 5 + }, + "id": 10, + "panels": [] + }, + { + "type": "bargauge", + "id": 11, + "targets": [ + { + "expr": "namespace:gpu_count:allocated{namespace=~\"$namespace\"}", + "legendFormat": "{{namespace}}", + "refId": "A" + } + ], + "title": "GPUs per namespace", + "description": "Kube GPU requests per tenant (billable). Includes Pending pods.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 6 + }, + "repeatDirection": "h", + "options": { + "displayMode": "gradient", + "valueMode": "color", + "namePlacement": "auto", + "showUnfilled": true, + "sizing": "auto", + "minVizWidth": 8, + "minVizHeight": 16, + "legend": { + "displayMode": "list", + "placement": "bottom", + "showLegend": false, + "calcs": [] + }, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "maxVizHeight": 300, + "orientation": "horizontal" + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "green" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "bargauge", + "id": 12, + "targets": [ + { + "expr": "node:fb_used_bytes:sum", + "legendFormat": "{{Hostname}}", + "refId": "A" + } + ], + "title": "VRAM used per node", + "description": "FB memory actively used per node. DCGM hardware metrics cannot be attributed to workload namespaces — shown per-node instead.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 6 + }, + "repeatDirection": "h", + "options": { + "displayMode": "gradient", + "valueMode": "color", + "namePlacement": "auto", + "showUnfilled": true, + "sizing": "auto", + "minVizWidth": 8, + "minVizHeight": 16, + "legend": { + "displayMode": "list", + "placement": "bottom", + "showLegend": false, + "calcs": [] + }, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "maxVizHeight": 300, + "orientation": "horizontal" + }, + "fieldConfig": { + "defaults": { + "unit": "bytes", + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "green" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "row", + "collapsed": false, + "title": "Utilization (per-node)", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 14 + }, + "id": 20, + "panels": [] + }, + { + "type": "timeseries", + "id": 21, + "targets": [ + { + "expr": "node:gpu_util:avg", + "legendFormat": "{{Hostname}}", + "refId": "A" + } + ], + "title": "NVML utilization per node", + "description": "Per-node NVML utilization. Stacked to show fleet-wide pressure over time. DCGM hardware metrics cannot be attributed to workload namespaces — shown per-node instead.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 15 + }, + "repeatDirection": "h", + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "showLegend": false, + "calcs": [ + "mean", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "" + } + }, + "fieldConfig": { + "defaults": { + "unit": "percent", + "min": 0, + "custom": { + "drawStyle": "line", + "lineInterpolation": "linear", + "fillOpacity": 40, + "showPoints": "never", + "stacking": { + "mode": "normal" + } + } + }, + "overrides": [] + } + }, + { + "type": "timeseries", + "id": 22, + "targets": [ + { + "expr": "node:tensor_active:avg * 100", + "legendFormat": "{{Hostname}}", + "refId": "A" + } + ], + "title": "Tensor saturation per node", + "description": "Real tensor core load per node. Useful to spot nodes with idle tensor cores. DCGM hardware metrics cannot be attributed to workload namespaces — shown per-node instead.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 15 + }, + "repeatDirection": "h", + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "showLegend": false, + "calcs": [ + "mean", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "" + } + }, + "fieldConfig": { + "defaults": { + "unit": "percent", + "min": 0, + "custom": { + "drawStyle": "line", + "lineInterpolation": "linear", + "fillOpacity": 40, + "showPoints": "never", + "stacking": { + "mode": "normal" + } + } + }, + "overrides": [] + } + }, + { + "type": "row", + "collapsed": false, + "title": "Power \u0026 allocation trend", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 23 + }, + "id": 30, + "panels": [] + }, + { + "type": "timeseries", + "id": 31, + "targets": [ + { + "expr": "node:power_watts:sum", + "legendFormat": "{{Hostname}}", + "refId": "A" + } + ], + "title": "Power draw per node", + "description": "Per-node GPU power draw. Stacked to see cluster-wide energy burn over time. DCGM hardware metrics cannot be attributed to workload namespaces — shown per-node instead.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 24 + }, + "repeatDirection": "h", + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "showLegend": false, + "calcs": [ + "mean", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "" + } + }, + "fieldConfig": { + "defaults": { + "unit": "watt", + "min": 0, + "custom": { + "drawStyle": "line", + "lineInterpolation": "linear", + "fillOpacity": 40, + "showPoints": "never", + "stacking": { + "mode": "normal" + } + } + }, + "overrides": [] + } + }, + { + "type": "timeseries", + "id": 32, + "targets": [ + { + "expr": "namespace:gpu_count:allocated{namespace=~\"$namespace\"}", + "legendFormat": "{{namespace}}", + "refId": "A" + } + ], + "title": "Allocated GPUs per namespace", + "description": "Kube-requested GPU count per tenant over time. Step-after interpolation reflects discrete allocation changes.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 24 + }, + "repeatDirection": "h", + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "showLegend": false, + "calcs": [ + "mean", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "" + } + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "min": 0, + "custom": { + "drawStyle": "line", + "lineInterpolation": "stepAfter", + "fillOpacity": 40, + "showPoints": "never", + "stacking": { + "mode": "normal" + } + } + }, + "overrides": [] + } + }, + { + "type": "row", + "collapsed": false, + "title": "Top consumers (live)", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 32 + }, + "id": 40, + "panels": [] + }, + { + "type": "table", + "id": 41, + "targets": [ + { + "expr": "namespace:gpu_count:allocated{namespace=~\"$namespace\"}", + "instant": true, + "range": false, + "format": "table", + "refId": "gpus" + } + ], + "title": "Top consumers (allocation)", + "description": "Live ranking of tenants by GPU allocation. Hardware metrics (util, tensor, power, VRAM) cannot be attributed to workload namespaces — see Fleet and Performance dashboards for per-node/per-GPU hardware views.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 33 + }, + "repeatDirection": "h", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "Time 1": true, + "Time 2": true, + "Time 3": true, + "Time 4": true, + "Time 5": true, + "__name__": true, + "__name__ 1": true, + "__name__ 2": true, + "__name__ 3": true, + "__name__ 4": true, + "__name__ 5": true, + "cluster": true, + "cluster 1": true, + "cluster 2": true, + "cluster 3": true, + "cluster 4": true, + "cluster 5": true, + "endpoint": true, + "endpoint 1": true, + "endpoint 2": true, + "endpoint 3": true, + "endpoint 4": true, + "endpoint 5": true, + "instance": true, + "instance 1": true, + "instance 2": true, + "instance 3": true, + "instance 4": true, + "instance 5": true, + "job": true, + "job 1": true, + "job 2": true, + "job 3": true, + "job 4": true, + "job 5": true, + "prometheus": true, + "prometheus 1": true, + "prometheus 2": true, + "prometheus 3": true, + "prometheus 4": true, + "prometheus 5": true, + "service": true, + "service 1": true, + "service 2": true, + "service 3": true, + "service 4": true, + "service 5": true, + "tenant": true, + "tenant 1": true, + "tenant 2": true, + "tenant 3": true, + "tenant 4": true, + "tenant 5": true, + "tier": true, + "tier 1": true, + "tier 2": true, + "tier 3": true, + "tier 4": true, + "tier 5": true, + "uid": true, + "uid 1": true, + "uid 2": true, + "uid 3": true, + "uid 4": true, + "uid 5": true, + "unit": true, + "unit 1": true, + "unit 2": true, + "unit 3": true, + "unit 4": true, + "unit 5": true + }, + "indexByName": { + "Value #gpus": 1, + "namespace": 0 + }, + "renameByName": { + "Value #gpus": "GPUs", + "namespace": "Namespace" + } + } + } + ], + "options": { + "frameIndex": 0, + "showHeader": true, + "showTypeIcons": false, + "sortBy": [ + { + "displayName": "GPUs", + "desc": true + } + ], + "footer": { + "show": false, + "reducer": [] + }, + "cellHeight": "sm" + } + }, + { + "type": "row", + "collapsed": false, + "title": "Historical usage (last 24h)", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 43 + }, + "id": 50, + "panels": [] + }, + { + "type": "bargauge", + "id": 51, + "targets": [ + { + "expr": "sum_over_time(namespace:gpu_count:allocated{namespace=~\"$namespace\"}[24h:1m]) / 60", + "legendFormat": "{{namespace}}", + "refId": "A" + } + ], + "title": "GPU-hours per namespace (last 24h)", + "description": "Billable GPU-hours per tenant over the last 24h window.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 44 + }, + "repeatDirection": "h", + "options": { + "displayMode": "gradient", + "valueMode": "color", + "namePlacement": "auto", + "showUnfilled": true, + "sizing": "auto", + "minVizWidth": 8, + "minVizHeight": 16, + "legend": { + "displayMode": "list", + "placement": "bottom", + "showLegend": false, + "calcs": [] + }, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "maxVizHeight": 300, + "orientation": "horizontal" + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "decimals": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "blue" + } + ] + } + }, + "overrides": [] + } + }, + { + "type": "bargauge", + "id": 52, + "targets": [ + { + "expr": "sum_over_time(node:power_watts:sum[24h:1m]) / 60 / 1000", + "legendFormat": "{{Hostname}}", + "refId": "A" + } + ], + "title": "Energy per node (last 24h)", + "description": "kWh consumed per node over the last 24h. Integrated from 1-minute power samples. DCGM hardware metrics cannot be attributed to workload namespaces — shown per-node instead.", + "transparent": false, + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 44 + }, + "repeatDirection": "h", + "options": { + "displayMode": "gradient", + "valueMode": "color", + "namePlacement": "auto", + "showUnfilled": true, + "sizing": "auto", + "minVizWidth": 8, + "minVizHeight": 16, + "legend": { + "displayMode": "list", + "placement": "bottom", + "showLegend": false, + "calcs": [] + }, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "maxVizHeight": 300, + "orientation": "horizontal" + }, + "fieldConfig": { + "defaults": { + "unit": "kwatth", + "decimals": 2, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": null, + "color": "green" + } + ] + } + }, + "overrides": [] + } + } + ], + "templating": { + "list": [ + { + "type": "datasource", + "name": "ds_prometheus", + "label": "Prometheus", + "skipUrlSync": false, + "query": "prometheus", + "current": { + "selected": false, + "text": "default", + "value": "default" + }, + "multi": false, + "allowCustomValue": true, + "includeAll": false, + "regex": "", + "auto": false, + "auto_min": "10s", + "auto_count": 30 + }, + { + "type": "query", + "name": "namespace", + "label": "Namespace", + "skipUrlSync": false, + "query": "label_values(namespace:gpu_count:allocated, namespace)", + "datasource": { + "type": "prometheus", + "uid": "$ds_prometheus" + }, + "current": { + "selected": true, + "text": "All", + "value": "$__all" + }, + "multi": true, + "allowCustomValue": true, + "refresh": 2, + "sort": 1, + "includeAll": true, + "auto": false, + "auto_min": "10s", + "auto_count": 30 + } + ] + }, + "annotations": {} +} diff --git a/dashboards/mongodb/mongodb-inmemory.json b/dashboards/mongodb/mongodb-inmemory.json new file mode 100644 index 00000000..c774a314 --- /dev/null +++ b/dashboards/mongodb/mongodb-inmemory.json @@ -0,0 +1,1640 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "description": "MongoDB WiredTiger InMemory Details", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 1, + "links": [ + { + "asDropdown": false, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": [ + "mongodb" + ], + "targetBlank": false, + "title": "Related Dashboards", + "type": "dashboards" + } + ], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "Amount of data currently stored InMemory in its uncompressed format.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "rgb(31, 120, 193)", + "mode": "fixed" + }, + "decimals": 2, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 0, + "y": 0 + }, + "hideTimeOverride": true, + "id": 1, + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": { + "valueSize": 20 + }, + "textMode": "auto", + "wideLayout": true + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "editorMode": "code", + "expr": "avg(mongodb_mongod_wiredtiger_cache_bytes{job=\"$job\", type=\"total\"})", + "interval": "5m", + "range": true, + "refId": "A" + } + ], + "timeFrom": "1m", + "title": "InMemory Data Size", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "Maximum size the InMemory data can grow to. Configurable via storage.wiredTiger.inMemory.cacheSizeGB.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "rgb(31, 120, 193)", + "mode": "fixed" + }, + "decimals": 2, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 6, + "y": 0 + }, + "hideTimeOverride": true, + "id": 2, + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": { + "valueSize": 20 + }, + "textMode": "auto", + "wideLayout": true + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "editorMode": "code", + "expr": "avg(mongodb_mongod_wiredtiger_cache_max_bytes{job=\"$job\"})", + "interval": "5m", + "range": true, + "refId": "A" + } + ], + "timeFrom": "1m", + "title": "InMemory Max Data Size", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "Amount of memory left available for InMemory databases.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 0, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgba(50, 172, 45, 0.97)", + "value": null + }, + { + "color": "rgba(237, 129, 40, 0.89)", + "value": 90 + }, + { + "color": "rgba(245, 54, 54, 0.9)", + "value": 95 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 3, + "interval": "$interval", + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": { + "valueSize": 20 + }, + "textMode": "auto", + "wideLayout": true + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "1-(avg(mongodb_mongod_wiredtiger_cache_bytes{job=\"$job\", type=\"total\"})/avg(mongodb_mongod_wiredtiger_cache_max_bytes{job=\"$job\"}))", + "interval": "5m", + "legendFormat": "", + "refId": "A" + } + ], + "title": "InMemory Available", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "Percentage of InMemory pages that have been changed and not yet had the modified data consolidated.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 0, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgba(42, 150, 37, 0.97)", + "value": null + }, + { + "color": "rgba(237, 129, 40, 0.89)", + "value": 30 + }, + { + "color": "rgba(245, 54, 54, 0.9)", + "value": 50 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "interval": "$interval", + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": { + "valueSize": 20 + }, + "textMode": "auto", + "wideLayout": true + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "avg(mongodb_mongod_wiredtiger_cache_pages{job=\"$job\",type=\"dirty\"})/avg(mongodb_mongod_wiredtiger_cache_pages{job=\"$job\",type=\"total\"})", + "interval": "5m", + "legendFormat": "", + "refId": "A" + } + ], + "title": "InMemory Dirty Pages", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "WiredTiger internal transactions", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 3 + }, + "id": 5, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "min" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "avg by (type) (rate(mongodb_mongod_wiredtiger_transactions_total{job=\"$job\"}[$interval]) or irate(mongodb_mongod_wiredtiger_transactions_total{job=\"$job\"}[5m]))", + "interval": "$interval", + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "title": "InMemory Transactions", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "Configured max and current size of the WiredTiger cache.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 3 + }, + "id": 6, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "min" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "avg(mongodb_mongod_wiredtiger_cache_max_bytes{job=\"$job\"})", + "interval": "$interval", + "legendFormat": "Maximum", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "avg(mongodb_mongod_wiredtiger_cache_bytes{job=\"$job\", type=\"total\"})", + "interval": "$interval", + "legendFormat": "Used", + "refId": "B" + } + ], + "title": "InMemory Capacity", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "Internal WiredTiger storage engine cursors and sessions currently open.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 11 + }, + "id": 7, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "min" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "avg(mongodb_mongod_wiredtiger_session_open_cursors_total{job=\"$job\"})", + "interval": "$interval", + "legendFormat": "Cursors", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "avg(mongodb_mongod_wiredtiger_session_open_sessions_total{job=\"$job\"})", + "interval": "$interval", + "legendFormat": "Sessions", + "refId": "B" + } + ], + "title": "InMemory Sessions", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "Pages in the WiredTiger cache", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 11 + }, + "id": 8, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "min" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "avg by (type) (mongodb_mongod_wiredtiger_cache_pages{job=\"$job\"})", + "interval": "$interval", + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "title": "InMemory Pages", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "A WiredTiger 'ticket' is assigned for every operation running simultaneously in the storage engine.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byRegexp", + "options": "/^write_/" + }, + "properties": [ + { + "id": "custom.transform", + "value": "negative-Y" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "min" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "avg by (type) (mongodb_mongod_wiredtiger_concurrent_transactions_total_tickets{job=\"$job\"}-mongodb_mongod_wiredtiger_concurrent_transactions_out_tickets{job=\"$job\"})", + "interval": "$interval", + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "title": "InMemory Concurrency Tickets", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "Operations queued due to a lock", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 19 + }, + "id": 10, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "min" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "avg by (type) (mongodb_mongod_global_lock_current_queue{job=\"$job\"})", + "interval": "$interval", + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "title": "Queued Operations", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "Documents per second inserted, updated, deleted or returned.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ops" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 27 + }, + "id": 11, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "min" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "avg by (state) (rate(mongodb_mongod_metrics_document_total{job=\"$job\"}[$interval]) or irate(mongodb_mongod_metrics_document_total{job=\"$job\"}[5m]))", + "interval": "$interval", + "legendFormat": "{{state}}", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "avg(rate(mongodb_mongod_op_counters_repl_total{job=\"$job\", type=\"delete\"}[$interval]) or irate(mongodb_mongod_op_counters_repl_total{job=\"$job\", type=\"delete\"}[5m]))", + "interval": "$interval", + "legendFormat": "repl_deleted", + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "avg(rate(mongodb_mongod_op_counters_repl_total{job=\"$job\", type=\"update\"}[$interval]) or irate(mongodb_mongod_op_counters_repl_total{job=\"$job\", type=\"update\"}[5m]))", + "interval": "$interval", + "legendFormat": "repl_updated", + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "avg(rate(mongodb_mongod_op_counters_repl_total{job=\"$job\", type=\"insert\"}[$interval]) or irate(mongodb_mongod_op_counters_repl_total{job=\"$job\", type=\"insert\"}[5m]))", + "interval": "$interval", + "legendFormat": "repl_inserted", + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "avg(rate(mongodb_mongod_metrics_ttl_deleted_documents_total{job=\"$job\"}[$interval]) or irate(mongodb_mongod_metrics_ttl_deleted_documents_total{job=\"$job\"}[5m]))", + "interval": "$interval", + "legendFormat": "ttl_deleted", + "refId": "E" + } + ], + "title": "Document Changes", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "Number of pages evicted from the WiredTiger cache. The InMemory storage engine only evicts modified pages.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "Pages / sec", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 60, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 27 + }, + "id": 12, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "min" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "avg(rate(mongodb_mongod_wiredtiger_cache_evicted_total{job=\"$job\",type=\"modified\"}[$interval]) or irate(mongodb_mongod_wiredtiger_cache_evicted_total{job=\"$job\",type=\"modified\"}[5m]))", + "interval": "$interval", + "legendFormat": "Evicted", + "refId": "A" + } + ], + "title": "InMemory Cache Eviction", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "Number of objects scanned (data and index) and documents moved to a new location.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ops" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 35 + }, + "id": 13, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "min" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "avg by (state) (rate(mongodb_mongod_metrics_query_executor_total{job=\"$job\"}[$interval]) or irate(mongodb_mongod_metrics_query_executor_total{job=\"$job\"}[5m]))", + "interval": "$interval", + "legendFormat": "{{state}}", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "avg(rate(mongodb_mongod_metrics_record_moves_total{job=\"$job\"}[$interval]) or irate(mongodb_mongod_metrics_record_moves_total{job=\"$job\"}[5m]))", + "interval": "$interval", + "legendFormat": "moved", + "refId": "B" + } + ], + "title": "Scanned and Moved Objects", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "Memory page faults. Not necessarily from MongoDB.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 35 + }, + "id": 14, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "min" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "avg(rate(mongodb_mongod_extra_info_page_faults_total{job=\"$job\"}[$interval]) or irate(mongodb_mongod_extra_info_page_faults_total{job=\"$job\"}[5m]) or rate(mongodb_extra_info_page_faults_total{job=\"$job\"}[$interval]) or irate(mongodb_extra_info_page_faults_total{job=\"$job\"}[5m]))", + "interval": "$interval", + "legendFormat": "Faults", + "refId": "A" + } + ], + "title": "Page Faults", + "type": "timeseries" + } + ], + "preload": false, + "refresh": "30s", + "schemaVersion": 39, + "tags": [ + "mongodb" + ], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "default", + "value": "default" + }, + "hide": 0, + "includeAll": false, + "label": "Datasource", + "multi": false, + "name": "ds_prometheus", + "options": [], + "query": "prometheus", + "queryValue": "", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "auto": true, + "auto_count": 200, + "auto_min": "1s", + "current": { + "selected": false, + "text": "auto", + "value": "$__auto_interval_interval" + }, + "hide": 0, + "label": "Interval", + "name": "interval", + "options": [ + { + "selected": true, + "text": "auto", + "value": "$__auto_interval_interval" + }, + { + "selected": false, + "text": "1s", + "value": "1s" + }, + { + "selected": false, + "text": "5s", + "value": "5s" + }, + { + "selected": false, + "text": "1m", + "value": "1m" + }, + { + "selected": false, + "text": "5m", + "value": "5m" + }, + { + "selected": false, + "text": "1h", + "value": "1h" + }, + { + "selected": false, + "text": "6h", + "value": "6h" + }, + { + "selected": false, + "text": "1d", + "value": "1d" + } + ], + "query": "1s,5s,1m,5m,1h,6h,1d", + "refresh": 2, + "skipUrlSync": false, + "type": "interval" + }, + { + "current": { + "selected": false, + "text": "", + "value": "" + }, + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "definition": "label_values(mongodb_up, job)", + "hide": 0, + "includeAll": false, + "label": "Service", + "multi": false, + "name": "job", + "options": [], + "query": { + "query": "label_values(mongodb_up, job)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "type": "query" + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "MongoDB InMemory Details", + "uid": "mongodb-inmemory", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/dashboards/mongodb/mongodb-overview.json b/dashboards/mongodb/mongodb-overview.json new file mode 100644 index 00000000..558c8490 --- /dev/null +++ b/dashboards/mongodb/mongodb-overview.json @@ -0,0 +1,1460 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "description": "MongoDB Overview Dashboard", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 1, + "links": [ + { + "asDropdown": false, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": [ + "mongodb" + ], + "targetBlank": false, + "title": "Related Dashboards", + "type": "dashboards" + } + ], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "Shows how many times a command is executed per second on average during the selected interval.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ops" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "min" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "editorMode": "code", + "expr": "rate(mongodb_mongod_op_counters_total{job=~\"$job\", type!=\"command\"}[$interval]) or \nirate(mongodb_mongod_op_counters_total{job=~\"$job\", type!=\"command\"}[5m]) or \nrate(mongodb_mongos_op_counters_total{job=~\"$job\", type!=\"command\"}[$interval]) or \nirate(mongodb_mongos_op_counters_total{job=~\"$job\", type!=\"command\"}[5m])", + "interval": "$interval", + "legendFormat": "{{type}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "rate(mongodb_mongod_op_counters_repl_total{job=~\"$job\", type!~\"(command|query|getmore)\"}[$interval]) or \nirate(mongodb_mongod_op_counters_repl_total{job=~\"$job\", type!~\"(command|query|getmore)\"}[5m]) or\nrate(mongodb_mongos_op_counters_repl_total{job=~\"$job\", type!~\"(command|query|getmore)\"}[$interval]) or \nirate(mongodb_mongos_op_counters_repl_total{job=~\"$job\", type!~\"(command|query|getmore)\"}[5m])", + "interval": "$interval", + "legendFormat": "repl_{{type}}", + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "rate(mongodb_mongod_metrics_ttl_deleted_documents_total{job=~\"$job\"}[$interval]) or \nirate(mongodb_mongod_metrics_ttl_deleted_documents_total{job=~\"$job\"}[5m]) or\nrate(mongodb_mongos_metrics_ttl_deleted_documents_total{job=~\"$job\"}[$interval]) or \nirate(mongodb_mongos_metrics_ttl_deleted_documents_total{job=~\"$job\"}[5m])", + "interval": "$interval", + "legendFormat": "ttl_delete", + "refId": "C" + } + ], + "title": "Command Operations", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "Keep in mind the hard limit on the maximum number of connections set by your distribution.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 7 + }, + "id": 2, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "min" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "editorMode": "code", + "expr": "mongodb_connections{job=~\"$job\", state=\"current\"}", + "interval": "$interval", + "legendFormat": "{{pod}}", + "range": true, + "refId": "A" + } + ], + "title": "Connections", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "Helps identify why connections are increasing. Shows active cursors compared to cursors being automatically killed after 10 minutes due to an application not closing the connection.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 7 + }, + "id": 3, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "min" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "mongodb_mongod_metrics_cursor_open{job=~\"$job\"} or\nmongodb_mongod_cursors{job=~\"$job\"} or\nmongodb_mongos_metrics_cursor_open{job=~\"$job\"} or \nmongodb_mongos_cursors{job=~\"$job\"}", + "interval": "$interval", + "legendFormat": "{{state}}", + "refId": "A" + } + ], + "title": "Cursors", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "When used in combination with 'Command Operations', this graph can help identify write amplification.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ops" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 14 + }, + "id": 4, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "min" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "rate(mongodb_mongod_metrics_document_total{job=~\"$job\"}[$interval]) or \nirate(mongodb_mongod_metrics_document_total{job=~\"$job\"}[5m])", + "interval": "$interval", + "legendFormat": "{{state}}", + "refId": "A" + } + ], + "title": "Document Operations", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "Any number of queued operations for long periods of time is an indication of possible issues. Find the cause and fix it before requests get stuck in the queue.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ops" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 14 + }, + "id": 5, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "min" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "mongodb_mongod_global_lock_current_queue{job=~\"$job\"}", + "interval": "$interval", + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "title": "Queued Operations", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "Ratio of scanned objects/indexes to returned documents. High values indicate inefficient queries.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 21 + }, + "id": 6, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "min" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "sum(increase(mongodb_mongod_metrics_query_executor_total{job=~\"$job\", state=\"scanned_objects\"}[5m]))/sum(increase(mongodb_mongod_metrics_document_total{job=~\"$job\", state=\"returned\"}[5m]))", + "interval": "$interval", + "legendFormat": "Document", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "sum(increase(mongodb_mongod_metrics_query_executor_total{job=~\"$job\", state=\"scanned\"}[5m]))/sum(increase(mongodb_mongod_metrics_document_total{job=~\"$job\", state=\"returned\"}[5m]))", + "interval": "$interval", + "legendFormat": "Index", + "refId": "B" + } + ], + "title": "Query Efficiency", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "Number of objects scanned during queries and number of documents moved.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ops" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 21 + }, + "id": 7, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "min" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "rate(mongodb_mongod_metrics_query_executor_total{job=~\"$job\"}[$interval]) or irate(mongodb_mongod_metrics_query_executor_total{job=~\"$job\"}[5m])", + "interval": "$interval", + "legendFormat": "{{state}}", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "rate(mongodb_mongod_metrics_record_moves_total{job=~\"$job\"}[$interval]) or irate(mongodb_mongod_metrics_record_moves_total{job=~\"$job\"}[5m])", + "interval": "$interval", + "legendFormat": "moved", + "refId": "B" + } + ], + "title": "Scanned and Moved Objects", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "Useful for write-heavy workloads to understand how long it takes to verify writes.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 28 + }, + "id": 8, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "min" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "rate(mongodb_mongod_metrics_get_last_error_wtime_total_milliseconds{job=~\"$job\"}[$interval]) or \nirate(mongodb_mongod_metrics_get_last_error_wtime_total_milliseconds{job=~\"$job\"}[5m]) or\nrate(mongodb_mongos_metrics_get_last_error_wtime_total_milliseconds{job=~\"$job\"}[$interval]) or \nirate(mongodb_mongos_metrics_get_last_error_wtime_total_milliseconds{job=~\"$job\"}[5m])", + "interval": "$interval", + "legendFormat": "Write Wait Time", + "refId": "A" + } + ], + "title": "getLastError Write Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "Useful for write-heavy workloads to understand how many concurrent writes are occurring.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ops" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 28 + }, + "id": 9, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "min" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "rate(mongodb_mongod_metrics_get_last_error_wtime_num_total{job=~\"$job\"}[$interval]) or \nirate(mongodb_mongod_metrics_get_last_error_wtime_num_total{job=~\"$job\"}[5m]) or\nrate(mongodb_mongos_metrics_get_last_error_wtime_num_total{job=~\"$job\"}[$interval]) or \nirate(mongodb_mongos_metrics_get_last_error_wtime_num_total{job=~\"$job\"}[5m])", + "interval": "$interval", + "legendFormat": "Total", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "rate(mongodb_mongod_metrics_get_last_error_wtimeouts_total{job=~\"$job\"}[$interval]) or\nirate(mongodb_mongod_metrics_get_last_error_wtimeouts_total{job=~\"$job\"}[5m]) or\nrate(mongodb_mongos_metrics_get_last_error_wtimeouts_total{job=~\"$job\"}[$interval]) or\nirate(mongodb_mongos_metrics_get_last_error_wtimeouts_total{job=~\"$job\"}[5m])", + "interval": "$interval", + "legendFormat": "Timeouts", + "refId": "B" + } + ], + "title": "getLastError Write Operations", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "Asserts are not important by themselves, but you can correlate spikes with other graphs.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 35 + }, + "id": 10, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "min" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "rate(mongodb_mongod_asserts_total{job=~\"$job\"}[$interval]) or \nirate(mongodb_mongod_asserts_total{job=~\"$job\"}[5m]) or\nrate(mongodb_mongos_asserts_total{job=~\"$job\"}[$interval]) or \nirate(mongodb_mongos_asserts_total{job=~\"$job\"}[5m])", + "interval": "$interval", + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "title": "Assert Events", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "description": "Page faults indicate that requests are processed from disk either because an index is missing or there is not enough memory for the data set.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 35 + }, + "id": 11, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "min" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "expr": "rate(mongodb_mongod_extra_info_page_faults_total{job=~\"$job\"}[$interval]) or \nirate(mongodb_mongod_extra_info_page_faults_total{job=~\"$job\"}[5m]) or\nrate(mongodb_mongos_extra_info_page_faults_total{job=~\"$job\"}[$interval]) or \nirate(mongodb_mongos_extra_info_page_faults_total{job=~\"$job\"}[5m])", + "interval": "$interval", + "legendFormat": "Faults", + "refId": "A" + } + ], + "title": "Page Faults", + "type": "timeseries" + } + ], + "preload": false, + "refresh": "30s", + "schemaVersion": 39, + "tags": [ + "mongodb" + ], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "default", + "value": "default" + }, + "hide": 0, + "includeAll": false, + "label": "Datasource", + "multi": false, + "name": "ds_prometheus", + "options": [], + "query": "prometheus", + "queryValue": "", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "auto": true, + "auto_count": 200, + "auto_min": "1s", + "current": { + "selected": false, + "text": "auto", + "value": "$__auto_interval_interval" + }, + "hide": 0, + "label": "Interval", + "name": "interval", + "options": [ + { + "selected": true, + "text": "auto", + "value": "$__auto_interval_interval" + }, + { + "selected": false, + "text": "1s", + "value": "1s" + }, + { + "selected": false, + "text": "5s", + "value": "5s" + }, + { + "selected": false, + "text": "1m", + "value": "1m" + }, + { + "selected": false, + "text": "5m", + "value": "5m" + }, + { + "selected": false, + "text": "1h", + "value": "1h" + }, + { + "selected": false, + "text": "6h", + "value": "6h" + }, + { + "selected": false, + "text": "1d", + "value": "1d" + } + ], + "query": "1s,5s,1m,5m,1h,6h,1d", + "refresh": 2, + "skipUrlSync": false, + "type": "interval" + }, + { + "current": { + "selected": false, + "text": "", + "value": "" + }, + "datasource": { + "type": "prometheus", + "uid": "${ds_prometheus}" + }, + "definition": "label_values(mongodb_up, job)", + "hide": 0, + "includeAll": true, + "label": "Service", + "multi": true, + "name": "job", + "options": [], + "query": { + "query": "label_values(mongodb_up, job)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "type": "query" + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "MongoDB Overview", + "uid": "mongodb-overview", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/agents/changelog.md b/docs/agents/changelog.md index feb70597..2fde1224 100644 --- a/docs/agents/changelog.md +++ b/docs/agents/changelog.md @@ -6,6 +6,20 @@ This file contains detailed instructions for AI-powered IDE on how to generate c Follow these instructions when the user explicitly asks to generate a changelog. +## Scope and boundaries + +**Your single deliverable is the file `docs/changelogs/v.md`.** Write the complete, verified changelog to that path. That is the entire task. Exit as soon as the file is written and verified against the checklist in Step 9. + +Unless the caller explicitly instructs otherwise: + +- **In the cozystack working tree**, do not run `git commit`, `git push`, `git checkout` (to switch branches), `git branch`, `git tag`, `git reset`, `git merge`, or `git rebase`. Do not write to local branches, tags, or HEAD. `git fetch` is expected and fine (see the read-only analysis list below). +- **Do not** push to any remote, open pull requests, or issue GitHub API write calls (POST / PATCH / DELETE) for any repository. +- In the cozystack working tree, the **only** file you create or modify is `docs/changelogs/v.md`. Cloning auxiliary repositories under `_repos/` for cross-repo analysis (see Step 6) is fine; local git operations inside those disposable clones (`git checkout`, `git pull`, etc.) are allowed — just never push from them or open PRs against them. + +The caller — a GitHub Actions workflow in CI, or a developer running you interactively — owns branching, committing, pushing, and PR creation. They will perform those actions after you exit. Do not pre-empt them even if the working tree looks ready. + +Read-only analysis is expected and encouraged: `git log`, `git show`, `git fetch`, `git diff`, `gh pr view`, `gh api` GET requests, and reading any file in the repository. + ## Required Tools Before generating changelogs, ensure you have access to `gh` (GitHub CLI) tool, which is used to fetch commit and PR author information. The GitHub CLI is used to correctly identify PR authors from commits and pull requests. @@ -22,7 +36,7 @@ When the user asks to generate a changelog, follow these steps in the specified - [ ] Step 5: Get the list of commits for the release period - [ ] Step 6: Check additional repositories (website is REQUIRED, optional repos if tags exist) - [ ] **MANDATORY**: Check website repository for documentation changes WITH authors and PR links via GitHub CLI - - [ ] **MANDATORY**: Check ALL optional repositories (talm, boot-to-talos, cozyhr, cozy-proxy) for tags during release period + - [ ] **MANDATORY**: Check ALL optional repositories (talm, boot-to-talos, cozyhr, cozy-proxy, external-apps-example, ansible-cozystack) for tags during release period - [ ] **MANDATORY**: For ALL commits from additional repos, get GitHub username via CLI, prioritizing PR author over commit author. - [ ] Step 7: Analyze commits (extract PR numbers, authors, user impact) - [ ] **MANDATORY**: For EVERY PR in main repo, get PR author via `gh pr view --json author --jq .author.login` (do NOT skip this step) @@ -148,6 +162,8 @@ Cozystack release may include changes from related repositories. Check and inclu - [https://github.com/cozystack/boot-to-talos](https://github.com/cozystack/boot-to-talos) - [https://github.com/cozystack/cozyhr](https://github.com/cozystack/cozyhr) - [https://github.com/cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) +- [https://github.com/cozystack/external-apps-example](https://github.com/cozystack/external-apps-example) +- [https://github.com/cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack) **⚠️ IMPORTANT**: You MUST check ALL optional repositories for tags created during the release period. Do NOT skip this step even if you think there might not be any tags. Use the process below to verify. @@ -195,7 +211,7 @@ Cozystack release may include changes from related repositories. Check and inclu 3. **For optional repositories, check if tags exist during release period:** - **⚠️ MANDATORY: You MUST check ALL optional repositories (talm, boot-to-talos, cozyhr, cozy-proxy). Do NOT skip any repository!** + **⚠️ MANDATORY: You MUST check ALL optional repositories (talm, boot-to-talos, cozyhr, cozy-proxy, external-apps-example, ansible-cozystack). Do NOT skip any repository!** **Use the helper script:** ```bash @@ -208,7 +224,7 @@ Cozystack release may include changes from related repositories. Check and inclu ``` The script will: - - Check ALL optional repositories (talm, boot-to-talos, cozyhr, cozy-proxy) + - Check ALL optional repositories (talm, boot-to-talos, cozyhr, cozy-proxy, external-apps-example, ansible-cozystack) - Look for tags created during the release period - Get commits between tags (if tags exist) or by date range (if no tags) - Extract PR numbers from commit messages @@ -569,7 +585,7 @@ Create a new changelog file in the format matching previous versions: - [ ] Step 5 completed: **ALL commits included** (including merge commits and backports) - do not skip any commits - [ ] Step 5 completed: **Backports identified and handled correctly** - original PR author used, both original and backport PR numbers included - [ ] Step 6 completed: Website repository checked for documentation changes WITH authors and PR links via GitHub CLI -- [ ] Step 6 completed: **ALL** optional repositories (talm, boot-to-talos, cozyhr, cozy-proxy) checked for tags during release period +- [ ] Step 6 completed: **ALL** optional repositories (talm, boot-to-talos, cozyhr, cozy-proxy, external-apps-example, ansible-cozystack) checked for tags during release period - [ ] Step 6 completed: For ALL commits from additional repos, GitHub username obtained via GitHub CLI (not skipped). For commits with PR numbers, PR author used via `gh pr view` (not commit author) - [ ] Step 7 completed: For EVERY PR in main repo (including backports), PR author obtained via `gh pr view --json author --jq .author.login` (not skipped or assumed). Commit author NOT used - always use PR author - [ ] Step 7 completed: **Backports verified** - for each backport PR, original PR found and original PR author used in changelog @@ -606,6 +622,8 @@ Create a new changelog file in the format matching previous versions: **Save the changelog:** Save the changelog to file `docs/changelogs/v.md` according to the version for which the changelog is being generated. +**Then exit.** Do not commit, push, create a branch, or open a pull request — the caller handles all git and GitHub operations after you return. See the "Scope and boundaries" section at the top of this document. + ### Important notes - **After fetch with --force** local tags are up-to-date, use them for work @@ -628,7 +646,7 @@ Save the changelog to file `docs/changelogs/v.md` according to the vers - **Additional repositories (Step 6) - MANDATORY**: - **⚠️ CRITICAL**: Always check the **website** repository for documentation changes during the release period. This is a required step and MUST NOT be skipped. - - **⚠️ CRITICAL**: You MUST check ALL optional repositories (talm, boot-to-talos, cozyhr, cozy-proxy) for tags during the release period. Do NOT skip any repository even if you think there might not be tags. + - **⚠️ CRITICAL**: You MUST check ALL optional repositories (talm, boot-to-talos, cozyhr, cozy-proxy, external-apps-example, ansible-cozystack) for tags during the release period. Do NOT skip any repository even if you think there might not be tags. - **CRITICAL**: For ALL entries from additional repositories (website and optional), you MUST: - **MANDATORY**: Extract PR number from commit message first - **MANDATORY**: For commits with PR numbers, ALWAYS use `gh pr view --repo cozystack/ --json author --jq .author.login` to get PR author (not commit author) @@ -637,7 +655,7 @@ Save the changelog to file `docs/changelogs/v.md` according to the vers - **MANDATORY**: Do NOT use commit author for PRs - always use PR author - Include PR link or commit hash reference - Format: `* **[repo] Description**: details ([**@username**](https://github.com/username) in cozystack/repo#123)` - - For **optional repositories** (talm, boot-to-talos, cozyhr, cozy-proxy), you MUST check ALL of them for tags during the release period. Use the loop provided in Step 6 to check each repository systematically. + - For **optional repositories** (talm, boot-to-talos, cozyhr, cozy-proxy, external-apps-example, ansible-cozystack), you MUST check ALL of them for tags during the release period. Use the loop provided in Step 6 to check each repository systematically. - When including changes from additional repositories, use the format: `[repo-name] Description` and link to the repository's PR/issue if available - **Prefer PR numbers over commit hashes**: For commits from additional repositories, extract PR number from commit message using GitHub API. Use PR format (`cozystack/website#123`) instead of commit hash (`cozystack/website@abc1234`) when available - **Never add entries without author and PR/commit reference**: Every entry from additional repositories must have both author and link diff --git a/docs/agents/contributing.md b/docs/agents/contributing.md index d5a4366e..88b062ea 100644 --- a/docs/agents/contributing.md +++ b/docs/agents/contributing.md @@ -1,167 +1,140 @@ -# Instructions for AI Agents +# Contributing Conventions for AI Agents -Guidelines for AI agents contributing to Cozystack. +Project-side conventions for commits, branches, and pull requests in Cozystack. ## Checklist for Creating a Pull Request -- [ ] Changes are made and tested -- [ ] Commit message uses correct `[component]` prefix +- [ ] Commit message follows Conventional Commits format - [ ] Commit is signed off with `--signoff` - [ ] Branch is rebased on `upstream/main` (no extra commits) - [ ] PR body includes description and release note -- [ ] PR is pushed and created with `gh pr create` +- [ ] Ran `make generate` in every package whose `values.yaml`, `values.schema.json`, `Chart.yaml`, or `README.md` was touched, and committed the regenerated files -## How to Commit and Create Pull Requests +## Regenerate Artifacts Before Committing -### 1. Make Your Changes +Several files in each package are produced by `make generate` from `values.yaml` + `values.schema.json` and must stay in sync with the hand-edited sources: -Edit the necessary files in the codebase. +- `packages/(apps|extra)//README.md` — regenerated by `cozyvalues-gen` (parameter table, formatting). +- `packages/(apps|extra)//values.schema.json` — `cozyvalues-gen` rewrites ordering and derived fields. +- `packages/system/-rd/cozyrds/.yaml` — produced by `hack/update-crd.sh`, which `make generate` invokes. -### 2. Commit with Proper Format - -Use the `[component]` prefix and `--signoff` flag: +**Before committing edits to any of those sources**, run `make generate` inside the package and stage the full diff: ```bash -git commit --signoff -m "[component] Brief description of changes" +make -C packages// generate +git add packages/// packages/system/-rd/ ``` -**Component prefixes:** -- System: `[dashboard]`, `[platform]`, `[cilium]`, `[kube-ovn]`, `[linstor]`, `[fluxcd]`, `[cluster-api]` -- Apps: `[postgres]`, `[mariadb]`, `[redis]`, `[kafka]`, `[clickhouse]`, `[virtual-machine]`, `[kubernetes]` -- Other: `[tests]`, `[ci]`, `[docs]`, `[maintenance]` +The repo's pre-commit CI job runs `make generate` in every package and then `git diff --exit-code`. Any unstaged generator output fails the job with exit code 123 and blocks the PR. Also rerun `make generate` after a `git commit --amend` if the amended change touched any of the sources above. + +To locate packages a WIP branch likely needs to be regenerated: + +```bash +git diff --name-only | xargs -n1 dirname | sort -u | grep ^packages/ +``` + +## Commit Format + +Follow [Conventional Commits](https://www.conventionalcommits.org/) with `--signoff`: + +```bash +git commit --signoff -m "type(scope): brief description" +``` + +**Types:** `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore` + +**Scopes** (examples — not an exhaustive list; pick the most specific scope that describes the change, and introduce a new one if a genuinely new area needs its own): + +- System, e.g.: `dashboard`, `platform`, `operator`, `cilium`, `kube-ovn`, `linstor`, `fluxcd`, `cluster-api` +- Apps, e.g.: `postgres`, `mariadb`, `redis`, `kafka`, `clickhouse`, `virtual-machine`, `kubernetes` +- Other, e.g.: `api`, `hack`, `tests`, `ci`, `docs`, `agents`, `maintenance` + +Breaking changes: append `!` after type/scope (`feat(api)!: ...`) or add a `BREAKING CHANGE:` footer. **Examples:** ```bash -git commit --signoff -m "[dashboard] Add config hash annotations to restart pods on config changes" -git commit --signoff -m "[postgres] Update operator to version 1.2.3" -git commit --signoff -m "[docs] Add installation guide" +git commit --signoff -m "feat(dashboard): add config hash annotations to restart pods on config changes" +git commit --signoff -m "fix(postgres): update operator to version 1.2.3" +git commit --signoff -m "docs(contributing): add installation guide" ``` -### 3. Rebase on upstream/main (if needed) +## PR Title Auto-Labeling -If your branch has extra commits, clean it up: +`.github/workflows/pr-labeler.yaml` parses the PR title on `opened`, `edited`, `reopened`, and `synchronize` events and applies labels additively (never removes). The title is expected to follow Conventional Commits — same format as commit messages above. + +**Type → `kind/*`:** + +| type | label | +| --------- | ------------------ | +| feat | kind/feature | +| fix | kind/bug | +| docs | kind/documentation | +| chore | kind/cleanup | +| refactor | kind/cleanup | +| style, perf, test, build, ci, revert | (no kind label) | + +**Scope → `area/*`** (full mapping in `.github/workflows/pr-labeler.yaml`): + +| scope (examples) | label | +| --- | --- | +| agents, ai | area/ai | +| api, cozystack-api | area/api | +| build | area/build | +| ci | area/ci | +| dashboard | area/dashboard | +| postgres, mariadb, redis, etcd, kafka, clickhouse, postgres-operator, mariadb-operator | area/database | +| extra | area/extra | +| kubernetes | area/kubernetes | +| monitoring, vlogs, vmstack, grafana, workloadmonitor | area/monitoring | +| ingress, gateway, vpn, metallb, cilium, kube-ovn, cozy-proxy, ... | area/networking | +| platform, bundle, flux, fluxcd, cluster-api, talos, installer, cozyctl, cozystack-engine, cozy-lib | area/platform | +| backport, release | area/release | +| seaweedfs, bucket, linstor, velero, harbor, backups | area/storage | +| tests, e2e | area/testing | +| kubevirt, cdi, vmi, vm-import, virtual-machine, hami, gpu-operator | area/virtualization | + +**Special handling:** + +- `[Backport release-1.x]` prefix is stripped before parsing; `area/release` and `backport` labels are added. +- Composite scope (`feat(platform, system, apps): ...`) — each comma-separated part is mapped independently. +- `!` after type or `BREAKING CHANGE:` footer in the body → `kind/breaking-change`. +- Unmapped scope or non-conventional title → `area/uncategorized` (signals the PR needs manual area selection). +- Bracket-style fallback (`[scope] description`) maps `scope` → `area/*` but cannot infer `kind/*`. + +### AI Agent Attribution + +When an AI agent authors or materially assists with a commit, add an `Assisted-By:` trailer naming the model: + +```text +Assisted-By: Claude +Assisted-By: GPT-5 +Assisted-By: Gemini +``` + +This sits alongside the `Signed-off-by:` trailer produced by `--signoff`. Use one trailer per model if multiple contributed. + +## Rebasing on upstream/main + +If the branch has extra commits, clean it up: ```bash -# Fetch latest git fetch upstream - -# Create clean branch from upstream/main git checkout -b my-feature upstream/main - -# Cherry-pick only your commit git cherry-pick - -# Force push to your branch -git push -f origin my-feature:my-branch-name +git push -f origin my-feature ``` -### 4. Push Your Branch +## Pull Request Body -```bash -git push origin -``` +Fill in the template at [`.github/PULL_REQUEST_TEMPLATE.md`](../../.github/PULL_REQUEST_TEMPLATE.md). It includes the required `release-note` block. -### 5. Create Pull Request +Create the PR with `gh pr create --title "type(scope): brief description" --body-file `. -Write the PR body to a temporary file: +## Fetching Unresolved Review Comments -```bash -cat > /tmp/pr_body.md << 'EOF' -## What this PR does +Cozystack uses GitHub review threads with resolution status. Only unresolved threads are actionable — resolved threads are already handled. -Brief description of the changes. - -Changes: -- Change 1 -- Change 2 - -### Release note - -```release-note -[component] Description for changelog -``` -EOF -``` - -Create the PR: - -```bash -gh pr create --title "[component] Brief description" --body-file /tmp/pr_body.md -``` - -Clean up: - -```bash -rm /tmp/pr_body.md -``` - -## Addressing AI Bot Reviewer Comments - -When the user asks to fix comments from AI bot reviewers (like Qodo, Copilot, etc.): - -### 1. Get PR Comments - -View all comments on the pull request: - -```bash -gh pr view --comments -``` - -Or for the current branch: - -```bash -gh pr view --comments -``` - -### 2. Review Each Comment Carefully - -**Important**: Do NOT blindly apply all suggestions. Each comment should be evaluated: - -- **Consider context** - Does the suggestion make sense for this specific case? -- **Check project conventions** - Does it align with Cozystack patterns? -- **Evaluate impact** - Will this improve code quality or introduce issues? -- **Question validity** - AI bots can be wrong or miss context - -**When to apply:** -- ✅ Legitimate bugs or security issues -- ✅ Clear improvements to code quality -- ✅ Better error handling or edge cases -- ✅ Conformance to project conventions - -**When to skip:** -- ❌ Stylistic preferences that don't match project style -- ❌ Over-engineering simple code -- ❌ Changes that break existing patterns -- ❌ Suggestions that show misunderstanding of the code - -### 3. Apply Valid Fixes - -Make changes addressing the valid comments. Use your judgment. - -### 4. Leave Changes Uncommitted - -**Critical**: Do NOT commit or push the changes automatically. - -Leave the changes in the working directory so the user can: -- Review the fixes -- Decide whether to commit them -- Make additional adjustments if needed - -```bash -# After making changes, show status but DON'T commit -git status -git diff -``` - -The user will commit and push when ready. - -## Code Review Comments - -When asked to fix code review comments, **always work only with unresolved (open) comments**. Resolved comments should be ignored as they have already been addressed. - -### Getting Unresolved Review Comments - -Use GitHub GraphQL API to fetch only unresolved review comments from a pull request: +The REST endpoint `/pulls/{pr}/reviews` returns review summaries, not individual review comments. Use the GraphQL API to access `reviewThreads` with `isResolved` status: ```bash gh api graphql -F owner=cozystack -F repo=cozystack -F pr= -f query=' @@ -189,27 +162,7 @@ query($owner: String!, $repo: String!, $pr: Int!) { }' --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | .comments.nodes[]' ``` -### Filtering for Unresolved Comments - -The key filter is `select(.isResolved == false)` which ensures only unresolved review threads are processed. Each thread can contain multiple comments, but if the thread is resolved, all its comments should be ignored. - -### Working with Review Comments - -1. **Fetch unresolved comments** using the GraphQL query above -2. **Parse the results** to identify: - - File path (`path`) - - Line number (`line` or `originalLine`) - - Comment text (`bodyText`) - - Author (`author.login`) -3. **Address each unresolved comment** by: - - Locating the relevant code section - - Making the requested changes - - Ensuring the fix addresses the concern raised -4. **Do NOT process resolved comments** - they have already been handled - -### Example: Compact List of Unresolved Comments - -For a quick overview of unresolved comments: +Compact one-line variant: ```bash gh api graphql -F owner=cozystack -F repo=cozystack -F pr= -f query=' @@ -233,43 +186,3 @@ query($owner: String!, $repo: String!, $pr: Int!) { } }' --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | .comments.nodes[] | "\(.path):\(.line // "N/A") - \(.author.login): \(.bodyText[:150])"' ``` - -### Important Notes - -- **REST API limitation**: The REST endpoint `/pulls/{pr}/reviews` returns review summaries, not individual review comments. Use GraphQL API for accessing `reviewThreads` with `isResolved` status. -- **Thread-based resolution**: Comments are organized in threads. If a thread is resolved (`isResolved: true`), ignore all comments in that thread. -- **Always filter**: Never process comments from resolved threads, even if they appear in the results. - -### Example Workflow - -```bash -# Get PR comments -gh pr view 1234 --comments - -# Review comments and identify valid ones -# Make necessary changes to address valid comments -# ... edit files ... - -# Show what was changed (but don't commit) -git status -git diff - -# Tell the user what was fixed and what was skipped -``` - -## Git Permissions - -Request these permissions when needed: -- `git_write` - For commit, rebase, cherry-pick, branch operations -- `network` - For push, fetch, pull operations - -## Common Issues - -**PR has extra commits?** -→ Rebase on `upstream/main` and cherry-pick only your commits - -**Wrong commit message?** -→ `git commit --amend --signoff -m "[correct] message"` then `git push -f` - -**Need to update PR?** -→ `gh pr edit --body "new description"` diff --git a/docs/agents/overview.md b/docs/agents/overview.md index b6d69e68..ae0fd1c4 100644 --- a/docs/agents/overview.md +++ b/docs/agents/overview.md @@ -78,11 +78,18 @@ packages/// - Add proper error handling and structured logging ### Git Commits -- Use format: `[component] Description` +- Follow [Conventional Commits](https://www.conventionalcommits.org/) format: `type(scope): description` - Always use `--signoff` flag - Reference PR numbers when available - Keep commits atomic and focused -- Follow conventional commit format for changelogs + +### PackageSource CRD upgrade policy + +Each component in a `PackageSource` may set `install.upgradeCRDs` to control how CRDs from the chart's `crds/` directory are handled on `HelmRelease` upgrades. Allowed values: `Skip` (default — helm-controller does not touch CRDs on upgrade), `Create` (create new CRDs only), `CreateReplace` (create new and overwrite existing). + +Set `upgradeCRDs: CreateReplace` for operators whose upstream regularly adds new CRDs between versions (etcd-operator, cnpg, kubevirt, kamaji). Without it, new CRDs from a chart bump do not land on existing clusters — only fresh installs get them. + +Do **not** set `CreateReplace` blindly: it overwrites every CRD in `crds/` and can cause silent data loss if upstream drops a field from a CRD that has live objects. Only enable it for operators whose schema evolution is additive-only. When in doubt, leave it unset and apply new CRDs manually. ### Documentation diff --git a/docs/changelogs/v0.40.5.md b/docs/changelogs/v0.40.5.md new file mode 100644 index 00000000..40ebd107 --- /dev/null +++ b/docs/changelogs/v0.40.5.md @@ -0,0 +1,15 @@ + + +## Improvements + +* **[dashboard] Improve dashboard session params**: Improved session parameter handling in the dashboard for better user experience and more reliable session management ([**@lllamnyp**](https://github.com/lllamnyp) in #1913, #1919). + +## Dependencies + +* **Update cozyhr to v1.6.1**: Updated cozyhr to v1.6.1, which fixes a critical bug causing helm-controller v0.37.0+ to unexpectedly uninstall HelmReleases after cozyhr apply by correcting history snapshot fields for helm-controller compatibility ([**@kvaps**](https://github.com/kvaps) in cozystack/cozyhr#10). + +--- + +**Full Changelog**: [v0.40.4...v0.40.5](https://github.com/cozystack/cozystack/compare/v0.40.4...v0.40.5) diff --git a/docs/changelogs/v0.40.6.md b/docs/changelogs/v0.40.6.md new file mode 100644 index 00000000..40e499f5 --- /dev/null +++ b/docs/changelogs/v0.40.6.md @@ -0,0 +1,11 @@ + + +## Fixes + +* **[kubernetes] Fix manifests for kubernetes deployment**: Fixed incorrect manifests that prevented proper Kubernetes deployment, restoring correct application behavior ([**@IvanHunters**](https://github.com/IvanHunters) in #1943, #1944). + +--- + +**Full Changelog**: [v0.40.5...v0.40.6](https://github.com/cozystack/cozystack/compare/v0.40.5...v0.40.6) diff --git a/docs/changelogs/v0.40.7.md b/docs/changelogs/v0.40.7.md new file mode 100644 index 00000000..f1b7a52e --- /dev/null +++ b/docs/changelogs/v0.40.7.md @@ -0,0 +1,11 @@ + + +## Security + +* **[dashboard] Verify JWT token**: Added JWT token verification to the dashboard, ensuring that authentication tokens are properly validated before granting access. This prevents unauthorized access through forged or expired tokens ([**@lllamnyp**](https://github.com/lllamnyp) in #1980, #1984). + +--- + +**Full Changelog**: [v0.40.6...v0.40.7](https://github.com/cozystack/cozystack/compare/v0.40.6...v0.40.7) diff --git a/docs/changelogs/v0.41.4.md b/docs/changelogs/v0.41.4.md new file mode 100644 index 00000000..70f1daeb --- /dev/null +++ b/docs/changelogs/v0.41.4.md @@ -0,0 +1,11 @@ + + +## Dependencies + +* **Update cozyhr to v1.6.1**: Updated cozyhr to v1.6.1, which fixes a critical bug causing helm-controller v0.37.0+ to unexpectedly uninstall HelmReleases after cozyhr apply by correcting history snapshot fields for helm-controller compatibility ([**@kvaps**](https://github.com/kvaps) in cozystack/cozyhr#10). + +--- + +**Full Changelog**: [v0.41.3...v0.41.4](https://github.com/cozystack/cozystack/compare/v0.41.3...v0.41.4) diff --git a/docs/changelogs/v0.41.5.md b/docs/changelogs/v0.41.5.md new file mode 100644 index 00000000..fb96fcf4 --- /dev/null +++ b/docs/changelogs/v0.41.5.md @@ -0,0 +1,21 @@ + + +## Features and Improvements + +* **[dashboard] Add "Edit" button to all resources**: Added an "Edit" button across all resource views in the dashboard, allowing users to modify resource configurations directly from the UI ([**@sircthulhu**](https://github.com/sircthulhu) in #1928, #1931). + +* **[dashboard] Add resource quota usage to tenant details page**: Added resource quota usage display to the tenant details page, giving administrators visibility into how much of allocated resources each tenant is consuming ([**@sircthulhu**](https://github.com/sircthulhu) in #1929, #1932). + +* **[branding] Separate values for keycloak**: Separated Keycloak branding values into dedicated configuration, allowing more granular customization of Keycloak appearance without affecting other branding settings ([**@nbykov0**](https://github.com/nbykov0) in #1946). + +* **Add instance profile label to workload monitor**: Added instance profile metadata labels to the workload monitor, enabling better resource tracking and monitoring by instance profile type ([**@matthieu-robin**](https://github.com/matthieu-robin) in #1954, #1957). + +## Fixes + +* **[kubernetes] Fix manifests for kubernetes deployment**: Fixed incorrect manifests that prevented proper Kubernetes deployment, restoring correct application behavior ([**@IvanHunters**](https://github.com/IvanHunters) in #1943, #1945). + +--- + +**Full Changelog**: [v0.41.4...v0.41.5](https://github.com/cozystack/cozystack/compare/v0.41.4...v0.41.5) diff --git a/docs/changelogs/v0.41.6.md b/docs/changelogs/v0.41.6.md new file mode 100644 index 00000000..8e7783df --- /dev/null +++ b/docs/changelogs/v0.41.6.md @@ -0,0 +1,17 @@ + + +## Improvements + +* **[vm] Allow changing field external after creation**: Users can now modify the external network field on virtual machines after initial creation, providing more flexibility in VM networking configuration without requiring recreation ([**@sircthulhu**](https://github.com/sircthulhu) in #1956, #1962). + +* **[branding] Separate values for keycloak**: Separated Keycloak branding values into dedicated configuration for more granular customization of Keycloak appearance ([**@nbykov0**](https://github.com/nbykov0) in #1947, #1963). + +## Fixes + +* **[kubernetes] Fix coredns serviceaccount to match kubernetes bootstrap RBAC**: Configured the CoreDNS chart to create a `kube-dns` ServiceAccount matching the Kubernetes bootstrap ClusterRoleBinding, fixing RBAC errors (`Failed to watch`) when CoreDNS pods restart ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #1958, #1978). + +--- + +**Full Changelog**: [v0.41.5...v0.41.6](https://github.com/cozystack/cozystack/compare/v0.41.5...v0.41.6) diff --git a/docs/changelogs/v0.41.7.md b/docs/changelogs/v0.41.7.md new file mode 100644 index 00000000..5a77664d --- /dev/null +++ b/docs/changelogs/v0.41.7.md @@ -0,0 +1,15 @@ + + +## Security + +* **[dashboard] Verify JWT token**: Added JWT token verification to the dashboard, ensuring that authentication tokens are properly validated before granting access. This prevents unauthorized access through forged or expired tokens ([**@lllamnyp**](https://github.com/lllamnyp) in #1980, #1983). + +## Fixes + +* **[postgres-operator] Correct PromQL syntax in CNPGClusterOffline alert**: Fixed incorrect PromQL syntax in the `CNPGClusterOffline` alert rule for CloudNativePG, ensuring the alert fires correctly when all instances of a PostgreSQL cluster are offline ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #1981, #1989). + +--- + +**Full Changelog**: [v0.41.6...v0.41.7](https://github.com/cozystack/cozystack/compare/v0.41.6...v0.41.7) diff --git a/docs/changelogs/v0.41.8.md b/docs/changelogs/v0.41.8.md new file mode 100644 index 00000000..9984c87d --- /dev/null +++ b/docs/changelogs/v0.41.8.md @@ -0,0 +1,17 @@ + + +## Features and Improvements + +* **[kubernetes] Auto-enable Gateway API support in cert-manager**: cert-manager now automatically enables `enableGatewayAPI` when the Gateway API addon is active in the Kubernetes application. Users no longer need to manually configure this setting, and the option can still be overridden via `valuesOverride` ([**@kvaps**](https://github.com/kvaps) in #1997, #2012). + +* **[vm] Allow switching between instancetype and custom resources**: Users can now switch virtual machines between instancetype-based and custom resource configurations after creation. The upgrade hook atomically patches VM resources, providing more flexibility in adjusting VM sizing without recreation ([**@sircthulhu**](https://github.com/sircthulhu) in #2008, #2013). + +## Fixes + +* **[dashboard] Add startupProbe to prevent container restarts on slow hardware**: Added `startupProbe` to both `bff` and `web` containers in the dashboard deployment. On slow hardware, kubelet was killing containers because the `livenessProbe` only allowed ~33 seconds for startup. The `startupProbe` gives containers up to 60 seconds to start before `livenessProbe` kicks in ([**@kvaps**](https://github.com/kvaps) in #1996, #2014). + +--- + +**Full Changelog**: [v0.41.7...v0.41.8](https://github.com/cozystack/cozystack/compare/v0.41.7...v0.41.8) diff --git a/docs/changelogs/v0.41.9.md b/docs/changelogs/v0.41.9.md new file mode 100644 index 00000000..d0cd3ff9 --- /dev/null +++ b/docs/changelogs/v0.41.9.md @@ -0,0 +1,15 @@ + + +## Fixes + +* **[cozystack-basics] Deny resourcequotas deletion for tenant admin**: Prevented tenant administrators from deleting resource quotas, ensuring that resource limits set by platform administrators cannot be bypassed by tenant-level users ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2076). + +## Dependencies + +* **Update Kube-OVN to v1.15.3**: Updated Kube-OVN CNI to v1.15.3 with latest bug fixes and improvements ([**@kvaps**](https://github.com/kvaps)). + +--- + +**Full Changelog**: [v0.41.8...v0.41.9](https://github.com/cozystack/cozystack/compare/v0.41.8...v0.41.9) diff --git a/docs/changelogs/v1.0.0-rc.1.md b/docs/changelogs/v1.0.0-rc.1.md new file mode 100644 index 00000000..0fabe1b6 --- /dev/null +++ b/docs/changelogs/v1.0.0-rc.1.md @@ -0,0 +1,65 @@ + + +> **⚠️ Release Candidate Warning**: This is a release candidate intended for final validation before the stable v1.0.0 release. Breaking changes are not expected at this stage, but please test thoroughly before deploying to production. + +## Features and Improvements + +* **[harbor] Add managed Harbor container registry**: Added Harbor v2.14.2 as a managed tenant-level container registry service. The application uses CloudNativePG for PostgreSQL, the Redis operator for caching, and S3 via COSI BucketClaim (from SeaweedFS) for registry image storage. Auto-generated admin credentials are persisted across upgrades, TLS is handled by cert-manager, and Trivy vulnerability scanner is included. Users can now deploy a fully managed, production-ready OCI container registry within their tenant ([**@lexfrei**](https://github.com/lexfrei) in #2058). + +* **[kubernetes] Update supported Kubernetes versions to v1.30–v1.35**: Updated the tenant Kubernetes version matrix to v1.30, v1.31, v1.32, v1.33, v1.34, and v1.35 (now the default). EOL versions v1.28 and v1.29 are removed. Kamaji is updated to edge-26.2.4 with full Kubernetes 1.35 support, and the CAPI Kamaji provider is updated to v0.16.0. A compatibility patch ensures kubelets older than v1.35 are not broken by Kamaji injecting 1.35-specific kubelet fields ([**@lexfrei**](https://github.com/lexfrei) in #2073). + +* **[platform] Make cluster issuer name and ACME solver configurable**: Added `publishing.certificates.solver` (`http01` or `dns01`) and `publishing.certificates.issuerName` (default: `letsencrypt-prod`) parameters to the platform chart. This allows operators to point all ingress TLS annotations at any ClusterIssuer — custom ACME, self-signed, or internal CA — without modifying individual package templates. See the Breaking Changes section for the rename from the previous `issuerType` field ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2077). + +* **[dashboard] VMInstance dropdowns for disks and instanceType**: The VM instance creation form now renders API-backed dropdowns for the `instanceType` field (populated from `VirtualMachineClusterInstancetype` cluster resources) and for disk `name` fields (populated from `VMDisk` resources in the same namespace). Default values are read from the ApplicationDefinition's OpenAPI schema. This eliminates manual lookups and reduces misconfiguration when attaching disks or selecting VM instance types ([**@sircthulhu**](https://github.com/sircthulhu) in #2071). + +* **[installer] Remove CRDs from Helm chart, delegate lifecycle to operator**: The `cozy-installer` Helm chart no longer ships CRDs in its `crds/` directory. CRD lifecycle is now fully managed by the Cozystack operator via the `--install-crds` flag, which applies embedded CRD manifests on every startup using server-side apply. The platform PackageSource is also created by the operator instead of a Helm template. This ensures CRDs and the PackageSource are always up to date after each operator restart, eliminating stale CRDs from Helm's install-only behavior ([**@lexfrei**](https://github.com/lexfrei) in #2074). + +## Fixes + +* **[kubevirt] Update KubeVirt to v1.6.4 and CDI to v1.64.0, fix VM pod initialization**: Updated KubeVirt operator to v1.6.4 and CDI operator to v1.64.0, including live migration of existing VMs during the upgrade. Additionally, disabled serial console logging globally via the KubeVirt CR to prevent a known v1.6.x issue ([upstream #15989](https://github.com/kubevirt/kubevirt/issues/15989)) where the `guest-console-log` init container blocked virt-launcher pods from starting, causing all VMs to get stuck in `PodInitializing` state ([**@nbykov0**](https://github.com/nbykov0) in #1833; [**@kvaps**](https://github.com/kvaps) in 7dfb819). + +* **[linstor] Fix DRBD+LUKS+STORAGE resource creation failure**: All newly created encrypted volumes were failing because the DRBD `.res` file was never written due to a missing `setExists(true)` call in the `LuksLayer`. Applied the upstream `skip-adjust-when-device-inaccessible` patch ([LINBIT/linstor-server#477](https://github.com/LINBIT/linstor-server/pull/477)) which fixes the root cause and also prevents unnecessary lsblk calls when devices are not yet physically present ([**@kvaps**](https://github.com/kvaps) in #2072). + +* **[system] Fix monitoring-agents FQDN resolution for tenant workload clusters**: Monitoring agents (`vmagent`, `fluent-bit`) in tenant workload clusters were failing to deliver metrics and logs because service addresses used short DNS names without the cluster domain suffix. Fixed by appending the configured cluster domain from `_cluster.cluster-domain` (with fallback to `cluster.local`) to all vmagent remoteWrite URLs and fluent-bit output hosts ([**@IvanHunters**](https://github.com/IvanHunters) in #2075). + +* **[cozystack-basics] Preserve existing HelmRelease values during reconciliations**: Fixed a data-loss bug where changes made to the `tenant-root` HelmRelease were silently dropped on the next forced or upgrade reconciliation of the `cozystack-basics` HelmRelease. The reconciler now merges new configuration with existing values instead of overwriting them ([**@sircthulhu**](https://github.com/sircthulhu) in #2068). + +* **[cozystack-basics] Deny resourcequotas deletion for tenant admin**: Fixed the `cozy:tenant:admin:base` ClusterRole to explicitly deny deletion of `ResourceQuota` objects for tenant admins and superadmins, preventing accidental removal of tenant resource limits ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2076). + +## Breaking Changes & Upgrade Notes + +* **[platform] Certificate issuer configuration parameters renamed**: The `publishing.certificates.issuerType` field is renamed to `publishing.certificates.solver`, and the value `cloudflare` is renamed to `dns01` to align with standard ACME terminology. A new `publishing.certificates.issuerName` field (default: `letsencrypt-prod`) is introduced to allow pointing all ingresses at a custom ClusterIssuer. Migration 32 is included and automatically converts existing configurations during upgrade — no manual action is required ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2077). + +## Documentation + +* **[website] Migrate ConfigMap references to Platform Package in v1 documentation**: Updated the entire v1 documentation tree to replace legacy ConfigMap-based configuration references with the new Platform Package API, ensuring guides are consistent with the v1 configuration model ([**@sircthulhu**](https://github.com/sircthulhu) in cozystack/website#426). + +* **[website] Add generic Kubernetes deployment guide for v1**: Added a new installation guide covering Cozystack deployment on any generic Kubernetes cluster, expanding the set of supported deployment targets beyond provider-specific guides ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#408). + +* **[website] Refactor resource planning documentation**: Improved the resource planning guide with a clearer structure and more comprehensive coverage of planning considerations for Cozystack deployments ([**@IvanStukov**](https://github.com/IvanStukov) in cozystack/website#423). + +* **[website] Add ServiceAccount API access documentation and update FAQ**: Added a new article documenting ServiceAccount API access token configuration and updated the FAQ to include related troubleshooting guidance ([**@IvanStukov**](https://github.com/IvanStukov) in cozystack/website#421). + +* **[website] Update networking-mesh allowed-location-ips example**: Replaced provider-specific CLI usage with standard `kubectl` commands in the multi-location networking guide's `allowed-location-ips` example, making the documentation more universally applicable ([**@kvaps**](https://github.com/kvaps) in cozystack/website#425). + +## Contributors + +We'd like to thank all contributors who made this release possible: + +* [**@IvanHunters**](https://github.com/IvanHunters) +* [**@IvanStukov**](https://github.com/IvanStukov) +* [**@kvaps**](https://github.com/kvaps) +* [**@lexfrei**](https://github.com/lexfrei) +* [**@myasnikovdaniil**](https://github.com/myasnikovdaniil) +* [**@nbykov0**](https://github.com/nbykov0) +* [**@sircthulhu**](https://github.com/sircthulhu) + +### New Contributors + +We're excited to welcome our first-time contributors: + +* [**@myasnikovdaniil**](https://github.com/myasnikovdaniil) - First contribution! + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.0.0-beta.6...v1.0.0-rc.1 diff --git a/docs/changelogs/v1.0.0-rc.2.md b/docs/changelogs/v1.0.0-rc.2.md new file mode 100644 index 00000000..a056ff8f --- /dev/null +++ b/docs/changelogs/v1.0.0-rc.2.md @@ -0,0 +1,57 @@ + + +> **⚠️ Release Candidate Warning**: This is a release candidate intended for final validation before the stable v1.0.0 release. Breaking changes are not expected at this stage, but please test thoroughly before deploying to production. + +## Features and Improvements + +* **[keycloak] Allow custom Ingress hostname via values**: Added an `ingress.host` field to the cozy-keycloak chart values, allowing operators to override the default `keycloak.` Ingress hostname. The custom hostname is applied to both the Ingress resource and the `KC_HOSTNAME` environment variable in the StatefulSet. When left empty, the original behavior is preserved (fully backward compatible) ([**@sircthulhu**](https://github.com/sircthulhu) in #2101). + +## Fixes + +* **[platform] Fix upgrade issues in migrations, etcd timeout, and migration script**: Fixed multiple upgrade failures discovered during v0.41.1 → v1.0 upgrade testing. Migration 26 now uses the `cozystack.io/ui=true` label (always present on v0.41.1) instead of the new label that depends on migration 22 having run, and adds robust Helm secret deletion with fallback and verification. Migrations 28 and 29 wrap `grep` calls to prevent `pipefail` exits and fix the reconcile annotation to use RFC3339 format. Migration 27 now skips missing CRDs and adds a name-pattern fallback for Helm secret deletion. The etcd HelmRelease timeout is increased from 10m to 30m to accommodate TLS cert rotation hooks. The `migrate-to-version-1.0.sh` script gains the missing `bundle-disable`, `bundle-enable`, `expose-ingress`, and `expose-services` field mappings ([**@kvaps**](https://github.com/kvaps) in #2096). + +* **[platform] Fix orphaned -rd HelmReleases after application renames**: After the `ferretdb→mongodb`, `mysql→mariadb`, and `virtual-machine→vm-disk+vm-instance` renames, the system-level `-rd` HelmReleases in `cozy-system` (`ferretdb-rd`, `mysql-rd`, `virtual-machine-rd`) were left orphaned, referencing ExternalArtifacts that no longer exist and causing persistent reconciliation failures. Migrations 28 and 29 are updated to remove these resources, and migration 33 is added as a safety net for clusters that already passed those migrations ([**@kvaps**](https://github.com/kvaps) in #2102). + +* **[monitoring-agents] Fix FQDN resolution regression in tenant workload clusters**: The fix introduced in #2075 used `_cluster.cluster-domain` references in `values.yaml`, but `_cluster` values are not accessible from Helm subchart contexts — meaning fluent-bit received empty hostnames and failed to forward logs. This PR replaces the `_cluster` references with a new `global.clusterDomain` variable (empty by default for management clusters, set to the cluster domain for tenant clusters), which is correctly shared with all subcharts ([**@kvaps**](https://github.com/kvaps) in #2086). + +* **[dashboard] Fix legacy templating and cluster identifier in sidebar links**: Standardized the cluster identifier used across dashboard menu links, administration links, and API request paths, resolving incorrect or broken link targets for the Backups and External IPs sidebar sections ([**@androndo**](https://github.com/androndo) in #2093). + +* **[dashboard] Fix backupjobs creation form and sidebar backup category identifier**: Fixed the backup job creation form configuration, adding the required Name, Namespace, Plan Name, Application, and Backup Class fields. Fixed the sidebar backup category identifier that was causing incorrect navigation ([**@androndo**](https://github.com/androndo) in #2103). + +## Documentation + +* **[website] Add Helm chart development principles guide**: Added a new developer guide section documenting Cozystack's four core Helm chart principles: easy upstream updates, local-first artifacts, local dev/test workflow, and no external dependencies ([**@kvaps**](https://github.com/kvaps) in cozystack/website#418). + +* **[website] Add network architecture overview**: Added comprehensive network architecture documentation covering the multi-layered networking stack — MetalLB (L2/BGP), Cilium eBPF (kube-proxy replacement), Kube-OVN (centralized IPAM), and tenant isolation with identity-based eBPF policies — with Mermaid diagrams for all major traffic flows ([**@IvanHunters**](https://github.com/IvanHunters) in cozystack/website#422). + +* **[website] Update documentation to use jsonpatch for service exposure**: Improved `kubectl patch` commands throughout installation and configuration guides to use JSON Patch `add` operations for extending arrays instead of replacing them wholesale, making the documented commands safer and more precise ([**@sircthulhu**](https://github.com/sircthulhu) in cozystack/website#427). + +* **[website] Update certificates section in Platform Package documentation**: Updated the certificate configuration documentation to reflect the new `solver` and `issuerName` fields introduced in v1.0.0-rc.1, replacing the legacy `issuerType` references ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#429). + +* **[website] Add tenant Kubernetes cluster log querying guide**: Added documentation for querying logs from tenant Kubernetes clusters in Grafana using VictoriaLogs labels (`tenant`, `kubernetes_namespace_name`, `kubernetes_pod_name`), including the `monitoringAgents` addon prerequisite and step-by-step filtering examples ([**@IvanHunters**](https://github.com/IvanHunters) in cozystack/website#430). + +* **[website] Replace non-idempotent commands with idempotent alternatives**: Updated `helm install` to `helm upgrade --install`, `kubectl create -f` to `kubectl apply -f`, and `kubectl create ns` to the dry-run+apply pattern across all installation and deployment guides so commands can be safely re-run ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#431). + +* **[website] Fix broken documentation links with `.md` suffix**: Fixed incorrect internal links with `.md` suffix across virtualization guides for both v0 and v1 documentation, standardizing link text to "Developer Guide" ([**@cheese**](https://github.com/cheese) in cozystack/website#432). + +## Contributors + +We'd like to thank all contributors who made this release possible: + +* [**@androndo**](https://github.com/androndo) +* [**@cheese**](https://github.com/cheese) +* [**@IvanHunters**](https://github.com/IvanHunters) +* [**@kvaps**](https://github.com/kvaps) +* [**@lexfrei**](https://github.com/lexfrei) +* [**@myasnikovdaniil**](https://github.com/myasnikovdaniil) +* [**@sircthulhu**](https://github.com/sircthulhu) + +### New Contributors + +We're excited to welcome our first-time contributors: + +* [**@cheese**](https://github.com/cheese) - First contribution! + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.0.0-rc.1...v1.0.0-rc.2 diff --git a/docs/changelogs/v1.0.0.md b/docs/changelogs/v1.0.0.md new file mode 100644 index 00000000..469ea1db --- /dev/null +++ b/docs/changelogs/v1.0.0.md @@ -0,0 +1,289 @@ + + +# Cozystack v1.0.0 — "Stable" + +We are thrilled to announce **Cozystack v1.0.0**, the first stable major release of the Cozystack platform. This milestone represents a fundamental architectural evolution from the v0.x series, introducing a fully operator-driven package management system, a comprehensive backup and restore framework, a redesigned virtual machine architecture, and a rich set of new managed applications — all hardened through an extensive alpha, beta, and release-candidate cycle. + +## Feature Highlights + +### Package-Based Architecture with Cozystack Operator + +The most significant architectural change in v1.0.0 is the replacement of HelmRelease bundle deployments with a declarative **Package** and **PackageSource** model managed by the new `cozystack-operator`. Operators now define their platform configuration in a structured `values.yaml` and the operator reconciles the desired state by managing Package and PackageSource resources across the cluster. + +The operator also takes ownership of CRD lifecycle — installing and updating CRDs from embedded manifests at every startup — eliminating the stale-CRD problem that affected Helm-only installations. Flux sharding has been added to distribute tenant HelmRelease reconciliation across multiple Flux controllers, providing horizontal scalability in large multi-tenant environments. + +A migration script (`hack/migrate-to-version-1.0.sh`) is provided for upgrading existing v0.x clusters, along with 33 incremental migration steps that automate resource renaming, secret cleanup, and configuration conversion. + +### Comprehensive Backup and Restore System + +v1.0.0 ships a fully featured, production-ready backup and restore framework built on Velero integration. Users can define **BackupClass** resources to describe backup storage targets, create **BackupPlan** schedules, and trigger **RestoreJob** resources for end-to-end application recovery. + +Virtual machine backups are supported natively via the Velero KubeVirt plugin, which captures consistent VM disk snapshots alongside metadata. The backup controller and the backup strategy sub-controllers (including the VM-specific strategy) are installed by default, and a full dashboard UI allows users to monitor backup status, view backup job history, and initiate restore workflows. + +### Redesigned Virtual Machine Architecture + +The legacy `virtual-machine` application has been replaced with a two-resource architecture: **`vm-disk`** for managing persistent disks and **`vm-instance`** for managing VM lifecycle. This separation provides cleaner disk/instance management, allows disks to be reused across VM instances, and aligns with modern KubeVirt patterns. + +New capabilities include: a `cpuModel` field for direct CPU model specification without using an instanceType; the ability to switch between `instanceType`-based and custom resource-based configurations; migration from the deprecated `running` field to `runStrategy`; and native **RWX (NFS) filesystem support** in the KubeVirt CSI driver, enabling multiple pods to mount the same persistent volume simultaneously. + +### New Managed Applications + +v1.0.0 expands the application catalog significantly: + +- **MongoDB**: A fully managed MongoDB replica set with persistent storage, monitoring integration, and unified user/database configuration API. +- **Qdrant**: A high-performance vector database for AI and machine learning workloads, supporting single-replica and clustered modes with API key authentication and optional external LoadBalancer access. +- **Harbor**: A fully managed OCI container registry backed by CloudNativePG, Redis operator, and COSI BucketClaim (SeaweedFS). Includes Trivy vulnerability scanner, auto-generated admin credentials, and TLS via cert-manager. +- **NATS**: Enhanced with full Grafana monitoring dashboards for JetStream and server metrics, Prometheus support with TLS-aware configuration, and updated image customization options. +- **MariaDB**: The `mysql` application is renamed to `mariadb`, accurately reflecting the underlying engine. An automatic migration (migration 27) converts all existing MySQL resources to use the `mariadb` naming. + +FerretDB has been removed from the catalog as it is superseded by native MongoDB support. + +### Multi-Location Networking with Kilo and cilium-kilo + +Cozystack v1.0.0 introduces first-class support for multi-location clusters via the **Kilo** WireGuard mesh networking package. Kilo automatically establishes encrypted WireGuard tunnels between nodes in different network segments, enabling seamless cross-region communication. + +A new integrated **`cilium-kilo`** networking variant combines Cilium eBPF CNI with Kilo's WireGuard overlay in a single platform configuration selection. This variant enables `enable-ipip-termination` in Cilium and deploys Kilo with `--compatibility=cilium`, allowing Cilium network policies to function correctly over the WireGuard mesh — without any manual configuration of the two components. + +### Flux Sharding for Scalable Multi-Tenancy + +Tenant HelmRelease reconciliation is now distributed across multiple Flux controllers via sharding labels. Each tenant workload is assigned to a shard based on a deterministic hash, preventing a single Flux controller from becoming a bottleneck in large multi-tenant environments. The platform operator manages the shard assignment automatically, and new shards can be added by scaling the Flux deployment. + +## Major Features and Improvements + +### Cozystack Operator + +* **[cozystack-operator] Introduce Package and PackageSource APIs**: Added new CRDs for declarative package management, defining the full API for Package and PackageSource resources ([**@kvaps**](https://github.com/kvaps) in #1740, #1741, #1755, #1756, #1760, #1761). +* **[platform] Migrate from HelmRelease bundles to Package-based deployment**: Replaced HelmRelease bundle system with Package resources managed by cozystack-operator, including restructured values.yaml with full configuration support for networking, publishing, authentication, scheduling, branding, and resources ([**@kvaps**](https://github.com/kvaps) in #1816). +* **[cozystack-operator] Add automatic CRD installation at startup**: Added `--install-crds` flag to install embedded CRD manifests on every startup via server-side apply, ensuring CRDs and the PackageSource are always up to date ([**@lexfrei**](https://github.com/lexfrei) in #2060). +* **[installer] Remove CRDs from Helm chart, delegate lifecycle to operator**: The `cozy-installer` Helm chart no longer ships CRDs; CRD lifecycle is fully managed by the Cozystack operator ([**@lexfrei**](https://github.com/lexfrei) in #2074). +* **[cozystack-operator] Preserve existing suspend field in package reconciler**: Fixed package reconciler to properly preserve the suspend field state during reconciliation ([**@sircthulhu**](https://github.com/sircthulhu) in #2043). +* **[cozystack-operator] Fix namespace privileged flag resolution and field ownership**: Fixed operator to correctly check all Packages in a namespace when determining privileged status, and resolved SSA field ownership conflicts ([**@kvaps**](https://github.com/kvaps) in #2046). +* **[platform] Add flux-plunger controller**: Added flux-plunger controller to automatically fix stuck HelmRelease errors by cleaning up failed resources and retrying reconciliation ([**@kvaps**](https://github.com/kvaps) in #1843). +* **[installer] Add variant-aware templates for generic Kubernetes support**: Extended the installer to support generic and hosted Kubernetes deployments via the `cozystackOperator.variant=generic` parameter ([**@lexfrei**](https://github.com/lexfrei) in #2010). +* **[installer] Unify operator templates**: Merged separate operator templates into a single variant-based template supporting Talos and non-Talos deployments ([**@kvaps**](https://github.com/kvaps) in #2034). + +### API and Platform + +* **[api] Rename CozystackResourceDefinition to ApplicationDefinition**: Renamed CRD and all related types for clarity and consistency, with migration 24 handling the transition automatically ([**@kvaps**](https://github.com/kvaps) in #1864). +* **[platform] Add DNS-1035 validation for Application names**: Added dynamic DNS-1035 label validation for Application names at creation time, preventing resources with invalid names that would fail downstream ([**@lexfrei**](https://github.com/lexfrei) in #1771). +* **[platform] Make cluster issuer name and ACME solver configurable**: Added `publishing.certificates.solver` and `publishing.certificates.issuerName` parameters to allow pointing all ingress TLS annotations at any ClusterIssuer ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2077). +* **[platform] Add cilium-kilo networking variant**: Added integrated `cilium-kilo` networking variant combining Cilium CNI with Kilo WireGuard mesh overlay ([**@kvaps**](https://github.com/kvaps) in #2064). +* **[cozystack-api] Switch from DaemonSet to Deployment**: Migrated cozystack-api to a Deployment with PreferClose topology spread constraints, reducing resource consumption while maintaining high availability ([**@kvaps**](https://github.com/kvaps) in #2041, #2048). + +### Virtual Machines + +* **[vm-instance] Complete migration from virtual-machine to vm-disk and vm-instance**: Fully migrated from `virtual-machine` to the new `vm-disk` and `vm-instance` architecture, with automatic migration script (migration 28) for existing VMs ([**@kvaps**](https://github.com/kvaps) in #2040). +* **[kubevirt-csi-driver] Add RWX Filesystem (NFS) support**: Added Read-Write-Many filesystem support to kubevirt-csi-driver via automatic NFS server deployment per PVC ([**@kvaps**](https://github.com/kvaps) in #2042). +* **[vm] Add cpuModel field to specify CPU model without instanceType**: Added cpuModel field to VirtualMachine API for granular CPU control ([**@sircthulhu**](https://github.com/sircthulhu) in #2007). +* **[vm] Allow switching between instancetype and custom resources**: Implemented atomic upgrade hook for switching between instanceType-based and custom resource VM configurations ([**@sircthulhu**](https://github.com/sircthulhu) in #2008). +* **[vm] Migrate to runStrategy instead of running**: Migrated VirtualMachine API from deprecated `running` field to `runStrategy` ([**@sircthulhu**](https://github.com/sircthulhu) in #2004). +* **[vm] Always expose VMs with a service**: Virtual machines are now always exposed with at least a ClusterIP service, ensuring in-cluster DNS names ([**@lllamnyp**](https://github.com/lllamnyp) in #1738, #1751). +* **[dashboard] VMInstance dropdowns for disks and instanceType**: VM instance creation form now renders API-backed dropdowns for `instanceType` and disk `name` fields ([**@sircthulhu**](https://github.com/sircthulhu) in #2071). + +### Backup System + +* **[backups] Implement comprehensive backup and restore functionality**: Core backup Plan controller, Velero strategy controller, RestoreJob resource with end-to-end restore workflows, and enhanced backup plans UI ([**@lllamnyp**](https://github.com/lllamnyp) in #1640, #1685, #1687, #1719, #1720, #1737, #1967; [**@androndo**](https://github.com/androndo) in #1762, #1967, #1968, #1811). +* **[backups] Add kubevirt plugin to velero**: Added KubeVirt plugin to Velero for consistent VM state and data snapshots ([**@lllamnyp**](https://github.com/lllamnyp) in #2017). +* **[backups] Install backupstrategy controller by default**: Enabled backupstrategy controller by default for automatic backup scheduling ([**@lllamnyp**](https://github.com/lllamnyp) in #2020). +* **[backups] Better selectors for VM strategy**: Improved VM backup strategy selectors for accurate and reliable backup targeting ([**@lllamnyp**](https://github.com/lllamnyp) in #2023). +* **[backups] Create RBAC for backup resources**: Added comprehensive RBAC configuration for backup operations and restore jobs ([**@lllamnyp**](https://github.com/lllamnyp) in #2018). + +### Networking + +* **[kilo] Introduce Kilo WireGuard mesh networking**: Added Kilo as a system package providing secure WireGuard-based VPN mesh for connecting Kubernetes nodes across different networks and regions ([**@kvaps**](https://github.com/kvaps) in #1691). +* **[kilo] Add Cilium compatibility variant**: Added `cilium` variant enabling Cilium-aware IPIP encapsulation for full network policy enforcement with Kilo mesh ([**@kvaps**](https://github.com/kvaps) in #2055). +* **[kilo] Update to v0.8.0 with configurable MTU**: Updated Kilo to v0.8.0 with configurable MTU parameter and performance improvements ([**@kvaps**](https://github.com/kvaps) in #2003, #2049, #2053). +* **[local-ccm] Add local-ccm package**: Added local cloud controller manager for managing load balancer services in bare-metal environments ([**@kvaps**](https://github.com/kvaps) in #1831). +* **[local-ccm] Add node-lifecycle-controller component**: Added optional node-lifecycle-controller that automatically deletes unreachable NotReady nodes, solving the "zombie" node problem in autoscaled clusters ([**@IvanHunters**](https://github.com/IvanHunters) in #1992). +* **[tenant] Allow egress to parent ingress pods**: Updated tenant network policies to allow egress traffic to parent cluster ingress pods ([**@lexfrei**](https://github.com/lexfrei) in #1765, #1776). + +### New Applications + +* **[mongodb] Add MongoDB managed application**: Added MongoDB as a fully managed database with replica sets, persistent storage, and unified user/database configuration ([**@lexfrei**](https://github.com/lexfrei) in #1822; [**@kvaps**](https://github.com/kvaps) in #1923). +* **[qdrant] Add Qdrant vector database**: Added Qdrant as a high-performance vector database for AI/ML workloads with API key authentication and optional LoadBalancer access ([**@lexfrei**](https://github.com/lexfrei) in #1987). +* **[harbor] Add managed Harbor container registry**: Added Harbor v2.14.2 as a managed tenant-level container registry with CloudNativePG, Redis operator, COSI BucketClaim storage, and Trivy scanner ([**@lexfrei**](https://github.com/lexfrei) in #2058). +* **[nats] Add monitoring**: Added Grafana dashboards for NATS JetStream and server metrics, Prometheus monitoring with TLS support ([**@klinch0**](https://github.com/klinch0) in #1381). +* **[mariadb] Rename mysql application to mariadb**: Renamed MySQL application to MariaDB with automatic migration (migration 27) for all existing resources ([**@kvaps**](https://github.com/kvaps) in #2026). +* **[ferretdb] Remove FerretDB application**: Removed FerretDB, superseded by native MongoDB support ([**@kvaps**](https://github.com/kvaps) in #2028). + +### Kubernetes and System Components + +* **[kubernetes] Update supported Kubernetes versions to v1.30–v1.35**: Updated the tenant Kubernetes version matrix, with v1.35 as the new default. Kamaji updated to edge-26.2.4 and CAPI Kamaji provider to v0.16.0 ([**@lexfrei**](https://github.com/lexfrei) in #2073). +* **[kubernetes] Auto-enable Gateway API support in cert-manager**: Added automatic Gateway API support in cert-manager for tenant clusters ([**@kvaps**](https://github.com/kvaps) in #1997). +* **[kubernetes] Use ingress-nginx nodeport service**: Changed tenant Kubernetes clusters to use ingress-nginx NodePort service for improved compatibility ([**@sircthulhu**](https://github.com/sircthulhu) in #1948). +* **[system] Add cluster-autoscaler for Hetzner and Azure**: Added cluster-autoscaler system package for automatically scaling management cluster nodes on Hetzner and Azure ([**@kvaps**](https://github.com/kvaps) in #1964). +* **[cluster-autoscaler] Enable enforce-node-group-min-size by default**: Ensures node groups are always scaled up to their configured minimum size ([**@kvaps**](https://github.com/kvaps) in #2050). +* **[system] Add clustersecret-operator package**: Added clustersecret-operator for managing secrets across multiple namespaces ([**@sircthulhu**](https://github.com/sircthulhu) in #2025). + +### Monitoring + +* **[monitoring] Enable monitoring for core components**: Enhanced monitoring capabilities with dashboards and metrics for core Cozystack components ([**@IvanHunters**](https://github.com/IvanHunters) in #1937). +* **[monitoring] Add SLACK_SEVERITY_FILTER and VMAgent for tenant monitoring**: Added SLACK_SEVERITY_FILTER for Slack alert filtering and VMAgent for tenant namespace metrics scraping ([**@IvanHunters**](https://github.com/IvanHunters) in #1712). +* **[monitoring-agents] Fix FQDN resolution for tenant workload clusters**: Fixed monitoring agents in tenant clusters to use full DNS names with cluster domain suffix ([**@IvanHunters**](https://github.com/IvanHunters) in #2075; [**@kvaps**](https://github.com/kvaps) in #2086). + +### Storage + +* **[linstor] Move CRDs to dedicated piraeus-operator-crds chart**: Moved LINSTOR CRDs to a dedicated chart, ensuring reliable installation of all CRDs including `linstorsatellites.io` ([**@kvaps**](https://github.com/kvaps) in #2036; [**@IvanHunters**](https://github.com/IvanHunters) in #1991). +* **[seaweedfs] Increase certificate duration to 10 years**: Increased SeaweedFS certificate validity to 10 years to reduce rotation overhead ([**@IvanHunters**](https://github.com/IvanHunters) in #1986). + +## Improvements + +* **[dashboard] Upgrade dashboard to version 1.4.0**: Updated Cozystack dashboard to v1.4.0 with new features and improvements ([**@sircthulhu**](https://github.com/sircthulhu) in #2051). +* **[dashboard] Hide Ingresses/Services/Secrets tabs when no selectors defined**: Tabs are now conditionally shown based on whether the ApplicationDefinition has resource selectors configured, reducing UI clutter ([**@kvaps**](https://github.com/kvaps) in #2087). +* **[dashboard] Add startupProbe to prevent container restarts on slow hardware**: Added startup probe to dashboard pods to prevent unnecessary restarts ([**@kvaps**](https://github.com/kvaps) in #1996). +* **[keycloak] Allow custom Ingress hostname via values**: Added `ingress.host` field to cozy-keycloak chart values for overriding the default `keycloak.` hostname ([**@sircthulhu**](https://github.com/sircthulhu) in #2101). +* **[branding] Separate values for Keycloak**: Separated Keycloak branding values for better customization capabilities ([**@nbykov0**](https://github.com/nbykov0) in #1947). +* **[rbac] Use hierarchical naming scheme**: Refactored RBAC to use hierarchical naming for cluster roles and role bindings ([**@lllamnyp**](https://github.com/lllamnyp) in #2019). +* **[tenant,rbac] Use shared clusterroles**: Refactored tenant RBAC to use shared ClusterRoles for improved consistency ([**@lllamnyp**](https://github.com/lllamnyp) in #1999). +* **[kubernetes] Increase default apiServer resourcesPreset to large**: Increased kube-apiserver resource preset to `large` for more reliable operation under higher workloads ([**@kvaps**](https://github.com/kvaps) in #1875). +* **[kubernetes] Increase kube-apiserver startup probe threshold**: Increased startup probe threshold to allow more time for API server readiness ([**@kvaps**](https://github.com/kvaps) in #1876). +* **[etcd] Increase probe thresholds for better recovery**: Increased etcd probe thresholds to improve cluster resilience during temporary slowdowns ([**@kvaps**](https://github.com/kvaps) in #1874). +* **[etcd-operator] Add vertical-pod-autoscaler dependency**: Added VPA as a dependency to etcd-operator for proper resource scaling ([**@sircthulhu**](https://github.com/sircthulhu) in #2047). +* **[cilium] Change cilium-operator replicas to 1**: Reduced Cilium operator replicas to decrease resource consumption in smaller deployments ([**@IvanHunters**](https://github.com/IvanHunters) in #1784). +* **[keycloak-configure,dashboard] Enable insecure TLS verification by default**: Made SSL certificate verification configurable with insecure mode enabled by default for local development ([**@IvanHunters**](https://github.com/IvanHunters) in #2005). +* **[platform] Split telemetry between operator and controller**: Separated telemetry collection for better metrics isolation ([**@kvaps**](https://github.com/kvaps) in #1869). +* **[system] Add resource requests and limits to etcd-defrag**: Added resource requests and limits to etcd-defrag job to prevent resource contention ([**@matthieu-robin**](https://github.com/matthieu-robin) in #1785, #1786). + +## Fixes + +* **[dashboard] Fix sidebar visibility on cluster-level pages**: Fixed broken URLs with double `//` on cluster-level pages by hiding namespace-scoped sidebar items when no tenant is selected ([**@sircthulhu**](https://github.com/sircthulhu) in #2106). +* **[platform] Fix upgrade issues in migrations, etcd timeout, and migration script**: Fixed multiple upgrade failures discovered during v0.41.1 → v1.0 upgrade testing, including migration 26-29 fixes, RFC3339 format for annotations, and extended etcd HelmRelease timeout to 30m ([**@kvaps**](https://github.com/kvaps) in #2096). +* **[platform] Fix orphaned -rd HelmReleases after application renames**: Migrations 28-29 updated to remove orphaned `-rd` HelmReleases in `cozy-system` after `ferretdb→mongodb`, `mysql→mariadb`, and `virtual-machine→vm-disk+vm-instance` renames, with migration 33 as a safety net ([**@kvaps**](https://github.com/kvaps) in #2102). +* **[platform] Adopt tenant-root into cozystack-basics during migration**: Added migration 31 to adopt existing `tenant-root` Namespace and HelmRelease into `cozystack-basics` for a safe v0.41.x → v1.0 upgrade path ([**@kvaps**](https://github.com/kvaps) in #2065). +* **[platform] Preserve tenant-root HelmRelease during migration**: Fixed data-loss risk during migration where `tenant-root` HelmRelease could be deleted ([**@sircthulhu**](https://github.com/sircthulhu) in #2063). +* **[platform] Fix cozystack-values secret race condition**: Fixed race condition in cozystack-values secret creation that could cause initialization failures ([**@lllamnyp**](https://github.com/lllamnyp) in #2024). +* **[cozystack-basics] Preserve existing HelmRelease values during reconciliations**: Fixed data-loss bug where changes to `tenant-root` HelmRelease were dropped on the next reconciliation ([**@sircthulhu**](https://github.com/sircthulhu) in #2068). +* **[cozystack-basics] Deny resourcequotas deletion for tenant admin**: Fixed `cozy:tenant:admin:base` ClusterRole to explicitly deny deletion of ResourceQuota objects ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2076). +* **[dashboard] Fix legacy templating and cluster identifier in sidebar links**: Standardized cluster identifier across dashboard menu links resolving broken link targets for Backups and External IPs ([**@androndo**](https://github.com/androndo) in #2093). +* **[dashboard] Fix backupjobs creation form and sidebar backup category identifier**: Fixed backup job creation form fields and fixed sidebar backup category identifier ([**@androndo**](https://github.com/androndo) in #2103). +* **[kubevirt] Update KubeVirt to v1.6.4 and CDI to v1.64.0, fix VM pod initialization**: Updated KubeVirt and CDI and disabled serial console logging globally to fix the `guest-console-log` init container blocking virt-launcher pods ([**@nbykov0**](https://github.com/nbykov0) in #1833; [**@kvaps**](https://github.com/kvaps)). +* **[linstor] Fix DRBD+LUKS+STORAGE resource creation failure**: Applied upstream fix for all newly created encrypted volumes failing due to missing `setExists(true)` call in `LuksLayer` ([**@kvaps**](https://github.com/kvaps) in #2072). +* **[platform] Clean up Helm secrets for removed releases**: Added cleanup logic to migration 23 to remove orphaned Helm secrets from removed `-rd` releases ([**@kvaps**](https://github.com/kvaps) in #2035). +* **[monitoring] Fix YAML parse error in vmagent template**: Fixed YAML parsing error in monitoring-agents vmagent template ([**@kvaps**](https://github.com/kvaps) in #2037). +* **[monitoring] Remove cozystack-controller dependency**: Fixed monitoring package to remove unnecessary cozystack-controller dependency ([**@IvanHunters**](https://github.com/IvanHunters) in #1990). +* **[monitoring] Remove duplicate dashboards.list**: Fixed duplicate dashboards.list configuration in extra/monitoring package ([**@IvanHunters**](https://github.com/IvanHunters) in #2016). +* **[linstor] Update piraeus-server patches with critical fixes**: Backported critical patches fixing edge cases in device management and DRBD resource handling ([**@kvaps**](https://github.com/kvaps) in #1850). +* **[apiserver] Fix Watch resourceVersion and bookmark handling**: Fixed Watch API handling of resourceVersion and bookmarks for proper event streaming ([**@kvaps**](https://github.com/kvaps) in #1860). +* **[bootbox] Auto-create bootbox-application as dependency**: Fixed bootbox package to automatically create required bootbox-application dependency ([**@kvaps**](https://github.com/kvaps) in #1974). +* **[postgres-operator] Correct PromQL syntax in CNPGClusterOffline alert**: Fixed incorrect PromQL syntax in the CNPGClusterOffline Prometheus alert ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #1981). +* **[coredns] Fix serviceaccount to match kubernetes bootstrap RBAC**: Fixed CoreDNS service account to correctly match Kubernetes bootstrap RBAC requirements ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #1958). +* **[dashboard] Verify JWT token**: Added JWT token verification to dashboard for improved security ([**@lllamnyp**](https://github.com/lllamnyp) in #1980). +* **[codegen] Fix missing gen_client in update-codegen.sh**: Fixed build error in `pkg/generated/applyconfiguration/utils.go` by including `gen_client` in the codegen script ([**@lexfrei**](https://github.com/lexfrei) in #2061). +* **[kubevirt-operator] Fix typo in VMNotRunningFor10Minutes alert**: Fixed typo in VM alert name ensuring proper alert triggering ([**@lexfrei**](https://github.com/lexfrei) in #1770, #1775). + +## Security + +* **[dashboard] Verify JWT token**: Added JWT token verification to the dashboard for improved authentication security ([**@lllamnyp**](https://github.com/lllamnyp) in #1980). + +## Dependencies + +* **[cilium] Update to v1.18.6**: Updated Cilium CNI to v1.18.6 with security fixes and performance improvements ([**@sircthulhu**](https://github.com/sircthulhu) in #1868). +* **[kube-ovn] Update to v1.15.3**: Updated Kube-OVN CNI to v1.15.3 with performance improvements and bug fixes ([**@kvaps**](https://github.com/kvaps) in #2022). +* **[kilo] Update to v0.8.0**: Updated Kilo WireGuard mesh to v0.8.0 with performance improvements and new compatibility features ([**@kvaps**](https://github.com/kvaps) in #2053). +* **Update Talos Linux to v1.12.1**: Updated Talos Linux to v1.12.1 with latest features and security patches ([**@kvaps**](https://github.com/kvaps) in #1877). + +## System Configuration + +* **[vpc] Migrate subnets definition from map to array format**: Migrated VPC subnets from `map[string]Subnet` to `[]Subnet` with explicit `name` field, with automatic migration via migration 30 ([**@kvaps**](https://github.com/kvaps) in #2052). +* **[migrations] Add migrations 23-33 for v1.0 upgrade path**: Added 11 incremental migrations handling CRD ownership, resource renaming, secret cleanup, Helm adoption, and configuration conversion for the v0.41.x → v1.0.0 upgrade path ([**@kvaps**](https://github.com/kvaps) in #1975, #2035, #2036, #2040, #2026, #2065, #2052, #2102). +* **[tenant] Run cleanup job from system namespace**: Moved tenant cleanup job to system namespace for improved security and resource isolation ([**@lllamnyp**](https://github.com/lllamnyp) in #1774, #1777). + +## Development, Testing, and CI/CD + +* **[ci] Use GitHub Copilot CLI for changelog generation**: Automated changelog generation using GitHub Copilot CLI ([**@androndo**](https://github.com/androndo) in #1753). +* **[ci] Choose runner conditional on label**: Added conditional runner selection in CI based on PR labels ([**@lllamnyp**](https://github.com/lllamnyp) in #1998). +* **[e2e] Use helm install instead of kubectl apply for cozystack installation**: Replaced static YAML apply flow with direct `helm upgrade --install` of the installer chart in E2E tests ([**@lexfrei**](https://github.com/lexfrei) in #2060). +* **[e2e] Make kubernetes test retries effective by cleaning up stale resources**: Fixed E2E test retries by adding pre-creation cleanup and increasing deployment wait timeout to 300s ([**@lexfrei**](https://github.com/lexfrei) in #2062). +* **[e2e] Increase HelmRelease readiness timeout for kubernetes test**: Increased HelmRelease readiness timeout to prevent false failures on slower hardware ([**@lexfrei**](https://github.com/lexfrei) in #2033). +* **[ci] Improve cozyreport functionality**: Enhanced cozyreport tool with improved reporting for CI/CD pipelines ([**@lllamnyp**](https://github.com/lllamnyp) in #2032). +* **feat(cozypkg): add cross-platform build targets with version injection**: Added cross-platform build targets for cozypkg/cozyhr tool for linux/amd64, linux/arm64, darwin/amd64, darwin/arm64 ([**@kvaps**](https://github.com/kvaps) in #1862). +* **refactor: move scripts to hack directory**: Reorganized scripts to the standard `hack/` location ([**@kvaps**](https://github.com/kvaps) in #1863). +* **Update CODEOWNERS**: Updated CODEOWNERS to include new maintainers ([**@lllamnyp**](https://github.com/lllamnyp) in #1972; [**@IvanHunters**](https://github.com/IvanHunters) in #2015). +* **[talm] Skip config loading for completion subcommands**: Fixed talm CLI to skip config loading for shell completion commands ([**@kitsunoff**](https://github.com/kitsunoff) in cozystack/talm#109). +* **[talm] Fix metadata.id type casting in physical_links_info**: Fixed Prometheus query to properly cast metadata.id to string for regexMatch operations ([**@kvaps**](https://github.com/kvaps) in cozystack/talm#110). + +## Documentation + +* **[website] Add documentation versioning**: Implemented comprehensive documentation versioning with separate v0 and v1 documentation trees and a version selector in the UI ([**@IvanStukov**](https://github.com/IvanStukov) in cozystack/website#415). +* **[website] Describe upgrade to v1.0**: Added detailed upgrade instructions for migrating from v0.x to v1.0 ([**@nbykov0**](https://github.com/nbykov0) in cozystack/website@21bbe84). +* **[website] Migrate ConfigMap references to Platform Package in v1 docs**: Updated entire v1 documentation to replace legacy ConfigMap-based configuration with the new Platform Package API ([**@sircthulhu**](https://github.com/sircthulhu) in cozystack/website#426). +* **[website] Add generic Kubernetes deployment guide for v1**: Added installation guide for deploying Cozystack on any generic Kubernetes cluster ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#408). +* **[website] Describe operator-based and HelmRelease-based package patterns**: Added development documentation explaining operator-based and HelmRelease-based package patterns ([**@kvaps**](https://github.com/kvaps) in cozystack/website#413). +* **[website] Add Helm chart development principles guide**: Added developer guide documenting Cozystack's four core Helm chart principles ([**@kvaps**](https://github.com/kvaps) in cozystack/website#418). +* **[website] Add network architecture overview**: Added comprehensive network architecture documentation covering the multi-layered networking stack with Mermaid diagrams ([**@IvanHunters**](https://github.com/IvanHunters) in cozystack/website#422). +* **[website] Add LINSTOR disk preparation guide**: Added comprehensive documentation for preparing disks for LINSTOR storage ([**@IvanHunters**](https://github.com/IvanHunters) in cozystack/website#411). +* **[website] Add Proxmox VM migration guide**: Added detailed guide for migrating virtual machines from Proxmox to Cozystack ([**@IvanHunters**](https://github.com/IvanHunters) in cozystack/website#410). +* **[website] Add cluster autoscaler documentation**: Added documentation for Hetzner setup with Talos, vSwitch, and Kilo mesh integration ([**@kvaps**](https://github.com/kvaps) in #1964). +* **[website] Improve Azure autoscaling troubleshooting guide**: Enhanced Azure autoscaling documentation with serial console instructions and `az vmss update --custom-data` guidance ([**@kvaps**](https://github.com/kvaps) in cozystack/website#424). +* **[website] Update multi-location documentation for cilium-kilo variant**: Updated multi-location networking docs to reflect the integrated `cilium-kilo` variant selection ([**@kvaps**](https://github.com/kvaps) in cozystack/website@02d63f0). +* **[website] Update documentation to use jsonpatch for service exposure**: Improved `kubectl patch` commands to use JSON Patch `add` operations ([**@sircthulhu**](https://github.com/sircthulhu) in cozystack/website#427). +* **[website] Update certificates section in Platform Package documentation**: Updated certificate configuration docs to reflect new `solver` and `issuerName` fields ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#429). +* **[website] Add tenant Kubernetes cluster log querying guide**: Added documentation for querying logs from tenant clusters in Grafana using VictoriaLogs labels ([**@IvanHunters**](https://github.com/IvanHunters) in cozystack/website#430). +* **[website] Replace non-idempotent commands with idempotent alternatives**: Updated `helm install` to `helm upgrade --install` and `kubectl create` to `kubectl apply` across all installation guides ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#431). +* **[website] Fix broken documentation links with .md suffix**: Fixed incorrect internal links across virtualization guides for v0 and v1 documentation ([**@cheese**](https://github.com/cheese) in cozystack/website#432). +* **[website] Refactor resource planning documentation**: Improved resource planning guide with clearer structure and more comprehensive coverage ([**@IvanStukov**](https://github.com/IvanStukov) in cozystack/website#423). +* **[website] Add ServiceAccount API access documentation and update FAQ**: Added documentation for ServiceAccount API access token configuration and updated FAQ ([**@IvanStukov**](https://github.com/IvanStukov) in cozystack/website#421). +* **[website] Update networking-mesh allowed-location-ips example**: Replaced provider-specific CLI with standard `kubectl` commands in multi-location networking guide ([**@kvaps**](https://github.com/kvaps) in cozystack/website#425). +* **[website] docs(storage): simplify NFS driver setup instructions**: Simplified NFS driver setup documentation ([**@kvaps**](https://github.com/kvaps) in cozystack/website#399). +* **[website] Add Hetzner RobotLB documentation**: Added documentation for configuring public IP with Hetzner RobotLB ([**@kvaps**](https://github.com/kvaps) in cozystack/website#394). +* **[website] Add documentation for creating and managing cloned VMs**: Added comprehensive guide for VM cloning operations ([**@sircthulhu**](https://github.com/sircthulhu) in cozystack/website#401). +* **[website] Update Talos installation docs for Hetzner and Servers.com**: Updated installation documentation for Hetzner and Servers.com environments ([**@kvaps**](https://github.com/kvaps) in cozystack/website#395). +* **[website] Add Hidora organization support details**: Added Hidora to the support page ([**@matthieu-robin**](https://github.com/matthieu-robin) in cozystack/website#397, cozystack/website#398). +* **[website] Check quotas before an upgrade**: Added troubleshooting documentation for checking resource quotas before upgrades ([**@nbykov0**](https://github.com/nbykov0) in cozystack/website#405). +* **[website] Update support documentation**: Updated support documentation with current contact information ([**@xrmtech-isk**](https://github.com/xrmtech-isk) in cozystack/website#420). +* **[website] Correct typo in kubeconfig reference in Kubernetes installation guide**: Fixed documentation typo in kubeconfig reference ([**@shkarface**](https://github.com/shkarface) in cozystack/website#414). + +## Breaking Changes & Upgrade Notes + +* **[api] CozystackResourceDefinition renamed to ApplicationDefinition**: The `CozystackResourceDefinition` CRD has been renamed to `ApplicationDefinition`. Migration 24 handles the transition automatically during upgrade ([**@kvaps**](https://github.com/kvaps) in #1864). + +* **[platform] Certificate issuer configuration parameters renamed**: The `publishing.certificates.issuerType` field is renamed to `publishing.certificates.solver`, and the value `cloudflare` is renamed to `dns01`. A new `publishing.certificates.issuerName` field (default: `letsencrypt-prod`) is added. Migration 32 automatically converts existing configurations — no manual action required ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2077). + +* **[vpc] VPC subnets definition migrated from map to array format**: VPC subnets are now defined as `[]Subnet` with an explicit `name` field instead of `map[string]Subnet`. Migration 30 handles the conversion automatically ([**@kvaps**](https://github.com/kvaps) in #2052). + +* **[vm] virtual-machine application replaced by vm-disk and vm-instance**: The legacy `virtual-machine` application has been fully replaced. Migration 28 automatically converts existing VMs to the new architecture ([**@kvaps**](https://github.com/kvaps) in #2040). + +* **[mysql] mysql application renamed to mariadb**: Existing MySQL deployments are automatically renamed to MariaDB via migration 27 ([**@kvaps**](https://github.com/kvaps) in #2026). + +### Upgrade Guide + +To upgrade from v0.41.x to v1.0.0: +1. **Backup your cluster** before upgrading. +2. Run the provided migration script: `hack/migrate-to-version-1.0.sh`. +3. The 33 incremental migration steps will automatically handle all resource renaming, configuration conversion, CRD adoption, and secret cleanup. +4. Refer to the [upgrade documentation](https://cozystack.io/docs/v1/upgrade) for detailed instructions and troubleshooting. + +## Contributors + +We'd like to thank all contributors who made this release possible: + +* [**@androndo**](https://github.com/androndo) +* [**@cheese**](https://github.com/cheese) +* [**@IvanHunters**](https://github.com/IvanHunters) +* [**@IvanStukov**](https://github.com/IvanStukov) +* [**@kitsunoff**](https://github.com/kitsunoff) +* [**@klinch0**](https://github.com/klinch0) +* [**@kvaps**](https://github.com/kvaps) +* [**@lexfrei**](https://github.com/lexfrei) +* [**@lllamnyp**](https://github.com/lllamnyp) +* [**@matthieu-robin**](https://github.com/matthieu-robin) +* [**@mattia-eleuteri**](https://github.com/mattia-eleuteri) +* [**@myasnikovdaniil**](https://github.com/myasnikovdaniil) +* [**@nbykov0**](https://github.com/nbykov0) +* [**@shkarface**](https://github.com/shkarface) +* [**@sircthulhu**](https://github.com/sircthulhu) +* [**@xrmtech-isk**](https://github.com/xrmtech-isk) + +### New Contributors + +We're excited to welcome our first-time contributors: + +* [**@cheese**](https://github.com/cheese) - First contribution! +* [**@IvanStukov**](https://github.com/IvanStukov) - First contribution! +* [**@kitsunoff**](https://github.com/kitsunoff) - First contribution! +* [**@shkarface**](https://github.com/shkarface) - First contribution! +* [**@xrmtech-isk**](https://github.com/xrmtech-isk) - First contribution! + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.41.0...v1.0.0 diff --git a/docs/changelogs/v1.0.1.md b/docs/changelogs/v1.0.1.md new file mode 100644 index 00000000..2faf2755 --- /dev/null +++ b/docs/changelogs/v1.0.1.md @@ -0,0 +1,21 @@ + + +## Fixes + +* **[platform] Prevent cozystack-version ConfigMap from deletion**: Added resource protection to prevent the `cozystack-version` ConfigMap from being accidentally deleted, improving platform stability and reliability ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2112, #2114). + +* **[installer] Add keep annotation to Namespace and update migration script**: Added `helm.sh/resource-policy: keep` annotation to the `cozy-system` Namespace in the installer Helm chart to prevent Helm from deleting the namespace (and all HelmReleases within it) when the installer release is removed. The v1.0 migration script is also updated to annotate the `cozy-system` namespace and `cozystack-version` ConfigMap with this policy before migration ([**@kvaps**](https://github.com/kvaps) in #2122, #2123). + +* **[dashboard] Add FlowSchema to exempt BFF from API throttling**: Added a `cozy-dashboard-exempt` FlowSchema to exempt the dashboard Back-End-for-Frontend (BFF) service account from Kubernetes API Priority and Fairness throttling. Previously, the BFF fell under the `workload-low` priority level, causing 429 (Too Many Requests) errors under load, resulting in dashboard unresponsiveness ([**@kvaps**](https://github.com/kvaps) in #2121, #2124). + +## Documentation + +* **[website] Replace bundles documentation with variants**: Renamed the "Bundles" documentation section to "Variants" to match current Cozystack terminology. Removed deprecated variants (`iaas-full`, `distro-full`, `distro-hosted`) and added new variants: `default` (PackageSources only, for manual package management via cozypkg) and `isp-full-generic` (full PaaS/IaaS on k3s, kubeadm, or RKE2). Updated all cross-references throughout the documentation ([**@kvaps**](https://github.com/kvaps) in cozystack/website#433). + +* **[website] Add step to protect namespace before upgrading**: Updated the cluster upgrade guide and v0.41→v1.0 migration guide with a required step to annotate the `cozy-system` namespace and `cozystack-version` ConfigMap with `helm.sh/resource-policy=keep` before running `helm upgrade`, preventing accidental namespace deletion ([**@kvaps**](https://github.com/kvaps) in cozystack/website#435). + +--- + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.0.0...v1.0.1 diff --git a/docs/changelogs/v1.0.2.md b/docs/changelogs/v1.0.2.md new file mode 100644 index 00000000..fad9275e --- /dev/null +++ b/docs/changelogs/v1.0.2.md @@ -0,0 +1,19 @@ + + +## Fixes + +* **[platform] Suspend cozy-proxy if it conflicts with installer release during migration**: Added a check in the v0.41→v1.0 migration script to detect and automatically suspend the `cozy-proxy` HelmRelease when its `releaseName` is set to `cozystack`, which conflicts with the installer release and would cause `cozystack-operator` deletion during the upgrade ([**@kvaps**](https://github.com/kvaps) in #2128, #2130). + +* **[platform] Fix off-by-one error in run-migrations script**: Fixed a bug in the migration runner where the first required migration was always skipped due to an off-by-one error in the migration range calculation, ensuring all upgrade steps execute correctly ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2126, #2132). + +* **[system] Fix Keycloak proxy configuration for v26.x**: Replaced the deprecated `KC_PROXY=edge` environment variable with `KC_PROXY_HEADERS=xforwarded` and `KC_HTTP_ENABLED=true` in the Keycloak StatefulSet template. `KC_PROXY` was removed in Keycloak 26.x, previously causing "Non-secure context detected" warnings and broken cookie handling when running behind a reverse proxy with TLS termination ([**@sircthulhu**](https://github.com/sircthulhu) in #2125, #2134). + +* **[dashboard] Allow clearing instanceType field and preserve newlines in secret copy**: Added `allowEmpty: true` to the `instanceType` field in the VMInstance form so users can explicitly clear it to use custom KubeVirt resources without a named instance type. Also fixed newline preservation when copying secrets with CMD+C ([**@sircthulhu**](https://github.com/sircthulhu) in #2135, #2137). + +* **[dashboard] Restore stock-instance sidebars for namespace-level pages**: Restored `stock-instance-api-form`, `stock-instance-api-table`, `stock-instance-builtin-form`, and `stock-instance-builtin-table` sidebar resources that were inadvertently removed in #2106. Without these sidebars, namespace-level pages such as Backup Plans rendered as empty pages with no interactive content ([**@sircthulhu**](https://github.com/sircthulhu) in #2136, #2138). + +--- + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.0.1...v1.0.2 diff --git a/docs/changelogs/v1.0.3.md b/docs/changelogs/v1.0.3.md new file mode 100644 index 00000000..27ae6720 --- /dev/null +++ b/docs/changelogs/v1.0.3.md @@ -0,0 +1,17 @@ + + +## Fixes + +* **[platform] Fix package name conversion in migration script**: Fixed the `migrate-to-version-1.0.sh` script to correctly prepend the `cozystack.` prefix when converting `BUNDLE_DISABLE` and `BUNDLE_ENABLE` package name lists, ensuring packages are properly identified during the v0.41→v1.0 upgrade ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2144, #2148). + +## Documentation + +* **[website] Add white labeling guide**: Added a comprehensive guide for configuring white labeling (branding) in Cozystack v1, covering Dashboard fields (`titleText`, `footerText`, `tenantText`, `logoText`, `logoSvg`, `iconSvg`) and Keycloak fields (`brandName`, `brandHtmlName`). Includes SVG preparation workflow with theme-aware template variables, portable base64 encoding, and migration notes from the v0 ConfigMap approach ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#441). + +* **[website] Actualize backup and recovery documentation**: Reworked the backup and recovery docs to be user-focused, separating operator and tenant workflows. Added tenant-facing documentation for `BackupJob` and `Plan` resources and status inspection commands, and added a new Velero administration guide for operators covering storage credentials and backup storage configuration ([**@androndo**](https://github.com/androndo) in cozystack/website#434). + +--- + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.0.2...v1.0.3 diff --git a/docs/changelogs/v1.0.4.md b/docs/changelogs/v1.0.4.md new file mode 100644 index 00000000..4c55e2f3 --- /dev/null +++ b/docs/changelogs/v1.0.4.md @@ -0,0 +1,25 @@ + + +## Fixes + +* **[system] Fix Keycloak probe crashloop with management port health endpoints**: Fixed a crashloop where Keycloak 26.x was endlessly restarting because liveness and readiness probes were sending HTTP requests to port 8080. Keycloak 26.x redirects all requests on port 8080 to `KC_HOSTNAME` (HTTPS), and since kubelet does not follow redirects, probes failed, eventually triggering container restarts. The fix switches probes to the dedicated management port 9000 (`/health/live`, `/health/ready`) enabled via `KC_HEALTH_ENABLED=true`, exposes management port 9000, and adds a `startupProbe` with appropriate failure thresholds for better startup tolerance ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #2162, #2178). + +* **[system] Fix etcd-operator deprecated kube-rbac-proxy image**: Replaced the deprecated `gcr.io/kubebuilder/kube-rbac-proxy:v0.16.0` image with `quay.io/brancz/kube-rbac-proxy:v0.18.1` in the vendored etcd-operator chart. The GCR-hosted image became unavailable after March 18, 2025, causing etcd-operator pods to fail on image pull ([**@kvaps**](https://github.com/kvaps) in #2181, #2183). + +* **[platform] Fix VM MAC address not preserved during virtual-machine to vm-instance migration**: During the `virtual-machine` → `vm-instance` migration (script 29), VM MAC addresses were not preserved. Kube-OVN reads MAC addresses exclusively from the pod annotation `ovn.kubernetes.io/mac_address`, not from `spec.macAddress` of the IP resource. Without this annotation, migrated VMs received a new random MAC address, breaking OS-level network configuration that matches by MAC (e.g., netplan). The fix adds a Helm `lookup` in the vm-instance chart template to read the Kube-OVN IP resource and automatically inject the MAC and IP addresses as pod annotations ([**@sircthulhu**](https://github.com/sircthulhu) in #2169, #2191). + +* **[dashboard] Fix External IPs page showing empty rows**: Fixed the External IPs administration page displaying empty rows instead of service data. The `EnrichedTable` configuration in the `external-ips` factory was using incorrect property names — replaced `clusterNamePartOfUrl` with `cluster` and changed `pathToItems` from array format to dot-path string format, matching the convention used by all other `EnrichedTable` instances ([**@IvanHunters**](https://github.com/IvanHunters) in #2175, #2192). + +* **[dashboard] Fix disabled/hidden state reset on MarketplacePanel reconciliation**: Fixed a bug where the dashboard controller was hardcoding `disabled=false` and `hidden=false` on every reconcile loop, overwriting changes made through the dashboard UI. Services disabled or hidden via the marketplace panel now correctly retain their state after controller reconciliation ([**@IvanHunters**](https://github.com/IvanHunters) in #2176, #2202). + +* **[dashboard] Fix hidden MarketplacePanel resources appearing in sidebar menu**: Fixed the sidebar navigation showing all resources regardless of their MarketplacePanel `hidden` state. The controller now fetches MarketplacePanels during sidebar reconciliation and filters out resources where `hidden=true`, ensuring that hiding a resource from the marketplace also removes it from the sidebar navigation. Listing failures are non-fatal — if the configuration fetch fails, no hiding is applied and the dashboard remains functional ([**@IvanHunters**](https://github.com/IvanHunters) in #2177, #2204). + +## Documentation + +* **[website] Add OIDC self-signed certificates configuration guide**: Added a comprehensive guide for configuring OIDC authentication with Keycloak when using self-signed certificates (the default in Cozystack). Covers Talos machine configuration with certificate mounting and host entries, kubelogin setup instructions, and a troubleshooting section. The guide is available for both v0 and v1 versioned documentation paths ([**@IvanHunters**](https://github.com/IvanHunters) in cozystack/website#443). + +--- + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.0.3...v1.0.4 diff --git a/docs/changelogs/v1.0.5.md b/docs/changelogs/v1.0.5.md new file mode 100644 index 00000000..51f01143 --- /dev/null +++ b/docs/changelogs/v1.0.5.md @@ -0,0 +1,29 @@ + + +## Fixes + +* **[api] Fix spurious OpenAPI post-processing errors for non-apps group versions**: The API server no longer logs false errors while generating OpenAPI specs for core and other non-`apps.cozystack.io` group versions. The post-processor now exits early when the base `Application` schemas are absent, reducing noisy startup logs without affecting application schema generation ([**@kvaps**](https://github.com/kvaps) in #2212, #2216). + +## Documentation + +* **[website] Add `DependenciesNotReady` troubleshooting and correct packages management build target**: Added a troubleshooting guide for packages stuck in `DependenciesNotReady`, including how to inspect operator logs and identify missing dependencies, and fixed the outdated `make image-cozystack` command to `make image-packages` in the packages management guide ([**@kvaps**](https://github.com/kvaps) in cozystack/website#450). + +* **[website] Clarify operator-first installation order**: Reordered the platform installation guide and tutorial so users install the Cozystack operator before preparing and applying the Platform Package, matching the rest of the installation docs and reducing setup confusion during fresh installs ([**@sircthulhu**](https://github.com/sircthulhu) in cozystack/website#449). + +* **[website] Add automated installation guide for Ansible**: Added end-to-end documentation for deploying Cozystack with the `cozystack.installer` Ansible collection, including inventory examples, distro-specific playbooks, configuration reference, verification steps, and explicit version pinning guidance to help operators automate installs safely ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#442). + +* **[website] Expand CA rotation operations guide**: Completed the CA rotation documentation with separate Talos and Kubernetes certificate rotation procedures, dry-run preview steps, and post-rotation guidance for fetching updated `talosconfig` and `kubeconfig` files after certificate changes ([**@kvaps**](https://github.com/kvaps) in cozystack/website#406). + +* **[website] Improve backup operations documentation**: Enhanced the operator backup and recovery guide with clearer Velero enablement steps, concrete provider and bucket examples, and more useful commands for inspecting backups, schedules, restores, CRD status, and logs ([**@androndo**](https://github.com/androndo) in cozystack/website#440). + +* **[website] Add custom metrics collection guide**: Added a monitoring guide showing how tenants can expose their own Prometheus exporters through `VMServiceScrape` and `VMPodScrape`, including namespace labeling requirements, example manifests, verification steps, and troubleshooting advice ([**@IvanHunters**](https://github.com/IvanHunters) in cozystack/website#444). + +* **[website] Document PackageSource and Package architecture**: Added a Key Concepts reference covering `PackageSource` and `Package` reconciliation flow, dependency handling, update propagation, rollback behavior, FluxPlunger recovery, and the `cozypkg` CLI for package management ([**@IvanHunters**](https://github.com/IvanHunters) in cozystack/website#445). + +* **[website] Refresh v1 application and platform documentation**: Fixed the documentation auto-update flow and published a broad v1 documentation refresh covering newly documented applications, updated naming and navigation, virtualization and platform content updates, and reorganized versioned docs pages ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#439). + +--- + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.0.4...v1.0.5 diff --git a/docs/changelogs/v1.0.6.md b/docs/changelogs/v1.0.6.md new file mode 100644 index 00000000..c65c5fd5 --- /dev/null +++ b/docs/changelogs/v1.0.6.md @@ -0,0 +1,21 @@ + + +## Fixes + +* **[kubernetes] Fix CiliumNetworkPolicy endpointSelector for multi-node RWX volumes**: When an NFS-backed RWX volume is published to multiple VMs, the network policy's `endpointSelector` was only capturing the first VM. Subsequent volume publications added owner references but never broadened the selector, causing Cilium to block NFS egress and making mounts hang on all nodes except the first. The fix switches from `matchLabels` to `matchExpressions` (`operator: In`) so the selector lists all VM names and is rebuilt whenever owner references change ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #2227, #2228). + +* **[dashboard] Fix dashboard authentication failures after secret recreation**: Added a `secret-hash` annotation containing the SHA256 hash of the client secret to the dashboard `KeycloakClient` resource. Without this annotation, if the `dashboard-client` Secret was recreated (e.g. after an upgrade or reinstall), the `KeycloakClient` spec stayed unchanged, the EDP Keycloak operator skipped reconciliation, and Keycloak kept the stale secret — causing dashboard authentication failures. Now any secret value change updates the annotation hash, triggering operator reconciliation and syncing the new secret to Keycloak ([**@sircthulhu**](https://github.com/sircthulhu) in #2231, #2240). + +* **[etcd] Fix defrag CronJob accumulating pods during cluster upgrades**: Added protective limits to the etcd defragmentation CronJob to prevent job pile-up when etcd is temporarily unavailable during upgrades. Without `concurrencyPolicy: Forbid`, new jobs kept being created hourly while previous ones were still failing, accumulating hundreds of running/failed pods across tenants. The fix adds `concurrencyPolicy: Forbid`, a `startingDeadlineSeconds: 300` guard against missed schedules, a 30-minute job timeout, and limits retries to 2 ([**@sircthulhu**](https://github.com/sircthulhu) in #2233, #2235). + +## Documentation + +* **[website] Document `keycloakInternalUrl` platform value**: Added documentation for the `authentication.oidc.keycloakInternalUrl` platform value to the Platform Package Reference, Self-Signed Certificates guide, and Enable OIDC Server pages. This value routes dashboard backend OIDC requests through the internal Keycloak service, which is useful in environments with self-signed certificates ([**@sircthulhu**](https://github.com/sircthulhu) in cozystack/website#452). + +* **[website] Publish Cozystack v1.0 release announcement**: Added the official Cozystack v1.0 release announcement blog post and supporting images, celebrating the first stable release of the platform ([**@tym83**](https://github.com/tym83) in cozystack/website#453, cozystack/website#454). + +--- + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.0.5...v1.0.6 diff --git a/docs/changelogs/v1.0.7.md b/docs/changelogs/v1.0.7.md new file mode 100644 index 00000000..942115bf --- /dev/null +++ b/docs/changelogs/v1.0.7.md @@ -0,0 +1,27 @@ + + +## Fixes + +* **[platform] Fix tenant admins unable to create FoundationDB, Harbor, MongoDB, OpenBAO, OpenSearch, Qdrant, and VPN applications**: The `cozy:tenant:admin:base` ClusterRole was missing RBAC entries for `foundationdbs`, `harbors`, `mongodbs`, `openbaos`, `opensearches`, `qdrants`, and `vpns` resources from `apps.cozystack.io`. Without these permissions, tenant admins could not create these applications — the "Add" button was inactive in the dashboard. The fix adds all seven missing resource verbs ([**@sircthulhu**](https://github.com/sircthulhu) in #2268, #2271). + +* **[system] Fix 403 error on Service details page for tenant users**: The `cozy:tenant:base` and `cozy:tenant:view:base` ClusterRoles were missing read permissions for `discovery.k8s.io/endpointslices`. The dashboard requests EndpointSlices to display the "Pod serving" section on the Service details page, and without this permission tenant users received a 403 error. The fix adds `get`, `list`, and `watch` verbs for endpointslices to both tenant roles ([**@sircthulhu**](https://github.com/sircthulhu) in #2257, #2284). + +* **[dashboard] Fix "Pod serving" table showing "Raw:" prefixes and "Invalid Date" on Service details page**: The EndpointSlice table on the service details page displayed raw data and broken timestamps because the `EnrichedTable` component referenced the `factory-kube-service-details-endpointslice` customization ID which had no corresponding `CustomColumnsOverride`. The fix adds column definitions for Pod (`.targetRef.name`), Addresses (`.addresses`), Ready (`.conditions.ready`), and Node (`.nodeName`) ([**@sircthulhu**](https://github.com/sircthulhu) in #2266, #2282). + +* **[dashboard] Fix broken backup menu links missing cluster context**: Backup resources (plans, backupjobs, backups) are not `ApplicationDefinitions`, so `ensureNavigation()` never created their `baseFactoriesMapping` entries. Without these mappings, the OpenUI frontend could not resolve the `{cluster}` context for backup pages, producing broken sidebar links with an empty cluster segment (e.g. `/openapi-ui//tenant-root/...` instead of `/openapi-ui/default/tenant-root/...`). The fix adds the three missing static entries to the Navigation resource ([**@sircthulhu**](https://github.com/sircthulhu) in #2232, #2270). + +* **[linstor] Fix swapped VMPodScrape job labels causing incorrect alerts**: The `job` labels in the `cozy-linstor` VictoriaMetrics `VMPodScrape` templates were swapped: `linstor-satellite` metrics were relabeled as `job=linstor-controller` and vice versa. This caused `linstorControllerOffline` alerts to fire against satellite endpoints (`:9942`) while reporting the controller as unreachable. The fix ensures `linstor-satellite` metrics keep `job=linstor-satellite` and `linstor-controller` metrics keep `job=linstor-controller`, restoring consistent alerting and dashboard semantics ([**@sasha-sup**](https://github.com/sasha-sup) in #2264, #2288). + +* **[piraeus-operator] Fix LINSTOR satellite alert annotations and reduce false-positive alerts**: Two issues in the LINSTOR alerts shipped by `cozy-piraeus-operator` were fixed. First, `linstorSatelliteErrorRate` used a non-existent `name` label in annotations, resulting in `Satellite ""` in alert notifications — corrected to use `{{ $labels.hostname }}`. Second, `linstorSatelliteErrorRate` produced false positives when the `linstor-controller` scrape flapped and historical `linstor_error_reports_count` counters reappeared inside the alert window — fixed by requiring stable `up{job="linstor-controller"}` for the full 15-minute window. Additionally, the controller availability alert was split to add a dedicated warning for metrics scrape failures with a 10-minute hold time to reduce transient noise ([**@sasha-sup**](https://github.com/sasha-sup) in #2265, #2287). + +## Documentation + +* **[website] Add Backup and Recovery guide for VMInstance and VMDisk**: Replaced the generic Kubernetes Backup and Recovery guide with a virtualization-focused Backup and Recovery doc covering VMInstance and VMDisk one-off and scheduled backups, restores, status checks, and troubleshooting (including Velero-related notes) ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#456). + +* **[website] Update developer guide with operator-driven architecture and OCIRepository/migration flow**: Rewrote the development guide to describe the operator-driven in-cluster architecture, bootstrap flow, operator responsibilities, and platform install/update sequence. Added documentation for OCIRepositories and the migration flow with migration hook examples and sequencing rules for pre-upgrade/install migrations. Also updated the concepts guide with the two-repository update model, dependency ordering rules, namespace creation behavior, and cluster-wide values injection ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#458). + +--- + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.0.6...v1.0.7 diff --git a/docs/changelogs/v1.1.0.md b/docs/changelogs/v1.1.0.md new file mode 100644 index 00000000..ad61c1a6 --- /dev/null +++ b/docs/changelogs/v1.1.0.md @@ -0,0 +1,126 @@ + + +# Cozystack v1.1.0 + +Cozystack v1.1.0 delivers a major expansion of the managed application catalog with **OpenBAO** (open-source HashiCorp Vault fork) for secrets management, comprehensive **tiered object storage** with SeaweedFS storage pools, a new bucket **user model** with per-user credentials and S3 login support, **RabbitMQ version selection**, and **MongoDB Grafana dashboards**. The dashboard gains storageClass dropdowns for all stateful apps. This release also incorporates all fixes from the v1.0.x patch series. + +## Feature Highlights + +### OpenBAO: Managed Secrets Management Service + +Cozystack now ships **OpenBAO** as a fully managed PaaS application — an open-source fork of HashiCorp Vault providing enterprise-grade secrets management. Users can deploy OpenBAO instances in standalone mode (single replica with file storage) or in high-availability Raft mode (multiple replicas with integrated Raft consensus), with the mode switching automatically based on the `replicas` field. + +Each OpenBAO instance gets TLS enabled by default via cert-manager self-signed certificates, with DNS SANs covering all service endpoints and pod addresses. The Vault injector and CSI provider are intentionally disabled (they are cluster-scoped components not safe for per-tenant use). OpenBAO requires manual initialization and unsealing by design — no auto-unseal is configured. + +A full end-to-end E2E test covers the complete lifecycle: deploy, wait for certificate and API readiness, init, unseal, verify, and cleanup. OpenBAO is available in the application catalog for tenant namespaces. + +### SeaweedFS Tiered Storage Pools + +SeaweedFS now supports **tiered storage pools** — operators can define separate storage pools per disk type (SSD, HDD, NVMe) in the `volume.pools` field (Simple topology) or `volume.zones[name].pools` (MultiZone topology). Each pool creates an additional Volume StatefulSet alongside the default one, with SeaweedFS distinguishing storage via the `-disk=` flag on volume servers. + +Each pool automatically generates its own set of COSI resources: a standard `BucketClass`, a `-lock` BucketClass (COMPLIANCE mode, 365-day retention), a read-write `BucketAccessClass`, and a `-readonly` BucketAccessClass. This allows applications to place data on specific storage tiers and request appropriate access policies per pool. + +In MultiZone topology, pools are defined per zone and each zone × pool combination creates a dedicated StatefulSet (e.g., `us-east-ssd`, `us-west-hdd`), with nodes selected via `topology.kubernetes.io/zone` labels. Existing deployments with no pools defined produce output identical to previous versions — no migration is required. + +### Bucket User Model with S3 Login + +The bucket application introduces a new **user model** for access management. Instead of a single implicit BucketAccess resource, operators now define a `users` map where each entry creates a dedicated `BucketAccess` with its own credentials secret and an optional `readonly` flag. The S3 Manager UI has been updated with a login screen that uses per-session credentials from the user's own secret, replacing the previous basic-auth approach. + +Two new bucket parameters are available: `locking` provisions from the `-lock` BucketClass (COMPLIANCE mode, 365-day object lock retention) for write-once-read-many use cases, and `storagePool` selects a specific pool's BucketClass for tiered storage placement. The COSI driver has been updated to v0.3.0 to support the new `diskType` parameter. + +**⚠️ Breaking change**: The implicit default BucketAccess resource is no longer created. Existing buckets that relied on the single auto-generated BucketAccess will need to explicitly define users in the `users` map after upgrading. + +### RabbitMQ Version Selection + +RabbitMQ instances now support a configurable **version selector** (`version` field with values: `v4.2`, `v4.1`, `v4.0`, `v3.13`; default `v4.2`). The chart validates the selection at deploy time and uses it to pin the runtime image, giving operators control over the RabbitMQ release channel per instance. An automatic migration backfills the `version` field on all existing RabbitMQ resources to `v4.2`. + +## Major Features and Improvements + +* **[apps] Add OpenBAO as a managed secrets management service**: Deployed as a PaaS application with standalone (file storage) and HA Raft modes, TLS enabled by default via cert-manager, injector and CSI provider disabled for tenant safety, and a full E2E lifecycle test ([**@lexfrei**](https://github.com/lexfrei) in #2059). + +* **[seaweedfs] Add storage pools support for tiered storage**: Added `volume.pools` (Simple) and `volume.zones[name].pools` (MultiZone) for per-disk-type StatefulSets, zone overrides (`nodeSelector`, `storageClass`, `dataCenter`), per-pool COSI BucketClass and BucketAccessClass resources, and bumped seaweedfs-cosi-driver to v0.3.0 ([**@sircthulhu**](https://github.com/sircthulhu) in #2097). + +* **[apps][system] Add bucket user model with locking and storage pool selection**: Replaced implicit BucketAccess with per-user `users` map, added `locking` and `storagePool` parameters, renamed COSI BucketClass suffix from `-worm` to `-lock`, added `-readonly` BucketAccessClass for all topologies, and updated S3 Manager with login screen using per-user credentials ([**@IvanHunters**](https://github.com/IvanHunters) in #2119). + +* **[rabbitmq] Add version selection for RabbitMQ instances**: Added `version` field (`v4.2`, `v4.1`, `v4.0`, `v3.13`) with chart-level validation, default `v4.2`, and an automatic migration to backfill the field on existing instances ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2092). + +* **[system] Add MongoDB Overview and InMemory Details Grafana dashboards**: Added two comprehensive Grafana dashboards for MongoDB monitoring — Overview (command operations, connections, cursors, query efficiency, write time) and InMemory Details (WiredTiger cache, transactions, concurrency, eviction). Dashboards are registered in `dashboards.list` for automatic GrafanaDashboard CRD generation ([**@IvanHunters**](https://github.com/IvanHunters) in #2158). + +* **[dashboard] Add storageClass dropdown for all stateful apps**: Replaced the free-text `storageClass` input with an API-backed dropdown listing available StorageClasses from the cluster. Affects ClickHouse, Harbor, HTTPCache, Kubernetes, MariaDB, MongoDB, NATS, OpenBAO, Postgres, Qdrant, RabbitMQ, Redis, VMDisk (top-level `storageClass`), FoundationDB (`storage.storageClass`), and Kafka (`kafka.storageClass`, `zookeeper.storageClass`) ([**@sircthulhu**](https://github.com/sircthulhu) in #2131). + +* **[bucket] Add readonly S3 access credentials**: Added a readonly `BucketAccessClass` to the SeaweedFS COSI chart and updated the bucket application to automatically provision two sets of S3 credentials per bucket: read-write (for UI) and readonly ([**@IvanHunters**](https://github.com/IvanHunters) in #2105). + +* **[dashboard] Hide sidebar on cluster-level pages when no tenant selected**: Fixed broken URLs with double `//` on the main cluster page (before tenant selection) by clearing `CUSTOMIZATION_SIDEBAR_FALLBACK_ID` so no sidebar renders when no namespace is selected ([**@sircthulhu**](https://github.com/sircthulhu) in #2106). + +* **[cert-manager] Update cert-manager to v1.19.3**: Upgraded cert-manager with new CRDs moved into a dedicated CRD package, added global `nodeSelector` and `hostUsers` (pod user-namespace isolation), and renamed `ServiceMonitor` targetPort default to `http-metrics` ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2070). + +* **[dashboard] Add backupClasses dropdown to Plan/BackupJob forms**: Replaced free-text input for `backupClass` field with an API-backed dropdown populated with available BackupClass resources, making it easier to select the correct backup target ([**@androndo**](https://github.com/androndo) in #2104). + +## Fixes + +* **[platform] Fix package name conversion in migration script**: Fixed the `migrate-to-version-1.0.sh` script to correctly prepend the `cozystack.` prefix when converting `BUNDLE_DISABLE` and `BUNDLE_ENABLE` package name lists, ensuring packages are properly identified during the v0.41→v1.0 upgrade ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2144, #2148). + +* **[backups] Fix RBAC for backup controllers**: Updated RBAC permissions for the backup strategy controller to support enhanced backup and restore capabilities, including Velero integration and status management ([**@androndo**](https://github.com/androndo) in #2145). + +* **[kubernetes] Set explicit MTU for Cilium in tenant clusters**: Set explicit MTU 1350 for Cilium in KubeVirt-based tenant Kubernetes clusters to prevent packet drops caused by VXLAN encapsulation overhead. Cilium's auto-detection does not account for VXLAN overhead (50 bytes) when the VM interface inherits MTU 1400 from the parent OVN/Geneve overlay, causing intermittent connectivity issues and HTTP 499 errors under load ([**@IvanHunters**](https://github.com/IvanHunters) in #2147). + +* **[platform] Prevent cozystack-version ConfigMap from deletion**: Added resource protection annotations to prevent the `cozystack-version` ConfigMap from being accidentally deleted, improving platform stability ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2112, #2114). + +* **[installer] Add keep annotation to Namespace and update migration script**: Added `helm.sh/resource-policy: keep` annotation to the `cozy-system` Namespace in the installer Helm chart to prevent Helm from deleting the namespace and all HelmReleases within it when the installer release is removed. The v1.0 migration script is also updated to annotate the namespace and `cozystack-version` ConfigMap before migration ([**@kvaps**](https://github.com/kvaps) in #2122, #2123). + +* **[dashboard] Add FlowSchema to exempt BFF from API throttling**: Added a `cozy-dashboard-exempt` FlowSchema to exempt the dashboard Back-End-for-Frontend service account from Kubernetes API Priority and Fairness throttling, preventing 429 errors under load ([**@kvaps**](https://github.com/kvaps) in #2121, #2124). + +* **[platform] Suspend cozy-proxy if it conflicts with installer release during migration**: Added a check in the v0.41→v1.0 migration script to detect and suspend the `cozy-proxy` HelmRelease when its `releaseName` is set to `cozystack`, which conflicts with the installer release and would cause `cozystack-operator` deletion during the upgrade ([**@kvaps**](https://github.com/kvaps) in #2128, #2130). + +* **[platform] Fix off-by-one error in run-migrations script**: Fixed a bug in the migration runner where the first required migration was always skipped due to an off-by-one error in the migration range calculation ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2126, #2132). + +* **[system] Fix Keycloak proxy configuration for v26.x**: Replaced the deprecated `KC_PROXY=edge` environment variable with `KC_PROXY_HEADERS=xforwarded` and `KC_HTTP_ENABLED=true` in the Keycloak StatefulSet. `KC_PROXY` was removed in Keycloak 26.x, previously causing "Non-secure context detected" warnings and broken cookie handling behind a reverse proxy with TLS termination ([**@sircthulhu**](https://github.com/sircthulhu) in #2125, #2134). + +* **[dashboard] Allow clearing instanceType field and preserve newlines in secret copy**: Added `allowEmpty: true` to the `instanceType` field in the VMInstance form so users can explicitly clear it to use custom KubeVirt resources without a named instance type. Also fixed newline preservation when copying secrets with CMD+C ([**@sircthulhu**](https://github.com/sircthulhu) in #2135, #2137). + +* **[dashboard] Restore stock-instance sidebars for namespace-level pages**: Restored `stock-instance-api-form`, `stock-instance-api-table`, `stock-instance-builtin-form`, and `stock-instance-builtin-table` sidebar resources that were inadvertently removed in #2106. Without these sidebars, namespace-level pages such as Backup Plans rendered as empty pages ([**@sircthulhu**](https://github.com/sircthulhu) in #2136, #2138). + +## System Configuration + +* **[platform] Disable private key rotation in CA certs**: Set `rotationPolicy: Never` for all CA/root certificates used by system components (ingress-nginx, linstor, linstor-scheduler, seaweedfs, victoria-metrics-operator, kubeovn-webhook, lineage-controller-webhook, cozystack-api, etcd, linstor API/internal) to prevent trust chain problems when CA certificates are reissued ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2113). + +## Development, Testing, and CI/CD + +* **[ci] Add debug improvements for CI tests**: Added extra debug commands for Kubernetes startup diagnostics and improved error output in CI test runs ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2111). + +## Documentation + +* **[website] Add object storage guide (pools, buckets, users)**: Added a comprehensive guide covering SeaweedFS object storage configuration including storage pools for tiered storage, bucket creation with access classes, per-user credential management, and credential rotation procedures ([**@sircthulhu**](https://github.com/sircthulhu) in cozystack/website#438). + +* **[website] Add Build Your Own Platform (BYOP) guide**: Added a new "Build Your Own Platform" guide and split the installation documentation into platform installation and BYOP sub-pages, with cross-references throughout the documentation ([**@kvaps**](https://github.com/kvaps) in cozystack/website#437). + +* **[website] Add white labeling guide**: Added a comprehensive guide for configuring white labeling (branding) in Cozystack v1, covering Dashboard fields (`titleText`, `footerText`, `tenantText`, `logoText`, `logoSvg`, `iconSvg`) and Keycloak fields (`brandName`, `brandHtmlName`). Includes SVG preparation workflow with theme-aware template variables and portable base64 encoding ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#441). + +* **[website] Actualize backup and recovery documentation**: Reworked the backup and recovery docs to be user-focused, separating operator and tenant workflows. Added tenant-facing documentation for `BackupJob` and `Plan` resources and a new Velero administration guide for operators ([**@androndo**](https://github.com/androndo) in cozystack/website#434). + +* **[website] Add step to protect namespace before upgrading**: Updated the cluster upgrade guide and v0.41→v1.0 migration guide with a required step to annotate the `cozy-system` namespace and `cozystack-version` ConfigMap with `helm.sh/resource-policy=keep` before running `helm upgrade` ([**@kvaps**](https://github.com/kvaps) in cozystack/website#435). + +* **[website] Replace bundles documentation with variants**: Renamed the "Bundles" documentation section to "Variants" to match current Cozystack terminology. Removed deprecated variants and added new ones: `default` and `isp-full-generic` ([**@kvaps**](https://github.com/kvaps) in cozystack/website#433). + +* **[website] Fix component values override instructions**: Corrected the component values override documentation to reflect current configuration patterns ([**@kvaps**](https://github.com/kvaps) in cozystack/website#436). + +## Breaking Changes & Upgrade Notes + +* **[bucket] Bucket user model now requires explicit user definitions**: The implicit default `BucketAccess` resource is no longer created automatically. Existing buckets that relied on a single auto-generated credential secret will need to define users explicitly in the `users` map after upgrading. Each user entry creates its own `BucketAccess` resource and credential secret (optionally with `readonly: true`). The COSI BucketClass suffix has also been renamed from `-worm` to `-lock` ([**@IvanHunters**](https://github.com/IvanHunters) in #2119). + +## Contributors + +We'd like to thank all contributors who made this release possible: + +* [**@androndo**](https://github.com/androndo) +* [**@IvanHunters**](https://github.com/IvanHunters) +* [**@kvaps**](https://github.com/kvaps) +* [**@lexfrei**](https://github.com/lexfrei) +* [**@myasnikovdaniil**](https://github.com/myasnikovdaniil) +* [**@sircthulhu**](https://github.com/sircthulhu) + +--- + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.0.0...v1.1.0 diff --git a/docs/changelogs/v1.1.1.md b/docs/changelogs/v1.1.1.md new file mode 100644 index 00000000..18bb6814 --- /dev/null +++ b/docs/changelogs/v1.1.1.md @@ -0,0 +1,23 @@ + + +## Fixes + +* **[dashboard] Fix hidden MarketplacePanel resources appearing in sidebar menu**: The sidebar was generated independently from MarketplacePanels, always showing all resources regardless of their `hidden` state. Fixed by fetching MarketplacePanels during sidebar reconciliation and skipping resources where `hidden=true`, so hiding a resource from the marketplace also removes it from the sidebar navigation ([**@IvanHunters**](https://github.com/IvanHunters) in #2177, #2203). + +* **[dashboard] Fix disabled/hidden state overwritten on every MarketplacePanel reconciliation**: The controller was hardcoding `disabled=false` and `hidden=false` on every reconciliation, silently overwriting any user changes made through the dashboard UI. Fixed by reading and preserving the current `disabled`/`hidden` values from the existing resource before updating ([**@IvanHunters**](https://github.com/IvanHunters) in #2176, #2201). + +* **[dashboard] Fix External IPs factory EnrichedTable rendering**: The external-IPs table displayed empty rows because the factory used incorrect `EnrichedTable` properties. Replaced `clusterNamePartOfUrl` with `cluster` and changed `pathToItems` from array to dot-path string format, consistent with all other working `EnrichedTable` instances ([**@IvanHunters**](https://github.com/IvanHunters) in #2175, #2193). + +* **[platform] Fix VM MAC address not preserved during virtual-machine to vm-instance migration**: Kube-OVN reads MAC address exclusively from the pod annotation `ovn.kubernetes.io/mac_address`, not from the IP resource `spec.macAddress`. Without the annotation, migrated VMs received a new random MAC, breaking OS-level network configurations that match by MAC (e.g. netplan). Added a Helm `lookup` for the Kube-OVN IP resource in the vm-instance chart so that MAC and IP addresses are automatically injected as pod annotations when the resource exists ([**@sircthulhu**](https://github.com/sircthulhu) in #2169, #2190). + +* **[etcd-operator] Replace deprecated kube-rbac-proxy image**: The `gcr.io/kubebuilder/kube-rbac-proxy` image became unavailable after Google Container Registry was deprecated. Replaced it with `quay.io/brancz/kube-rbac-proxy` from the original upstream author, restoring etcd-operator functionality ([**@kvaps**](https://github.com/kvaps) in #2181, #2182). + +* **[migrations] Handle missing RabbitMQ CRD in migration 34**: Migration 34 failed with an error when the `rabbitmqs.apps.cozystack.io` CRD did not exist — which occurs on clusters where RabbitMQ was never installed. Added a CRD presence check before attempting to list resources so that migration 34 completes cleanly on such clusters ([**@IvanHunters**](https://github.com/IvanHunters) in #2168, #2180). + +* **[keycloak] Fix Keycloak crashloop due to misconfigured health probes**: Keycloak 26.x redirects all HTTP requests on port 8080 to the configured HTTPS hostname; since kubelet does not follow redirects, liveness and readiness probes failed causing a crashloop. Fixed by enabling `KC_HEALTH_ENABLED=true`, exposing management port 9000, and switching all probes to `/health/live` and `/health/ready` on port 9000. Also added a `startupProbe` for improved startup tolerance ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #2162, #2179). + +--- + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.1.0...v1.1.1 diff --git a/docs/changelogs/v1.1.2.md b/docs/changelogs/v1.1.2.md new file mode 100644 index 00000000..29dba321 --- /dev/null +++ b/docs/changelogs/v1.1.2.md @@ -0,0 +1,25 @@ + + +## Fixes + +* **[bucket] Fix S3 Manager endpoint mismatch with COSI credentials**: The S3 Manager UI previously constructed an `s3..` endpoint even though COSI-issued bucket credentials point to the root-level S3 endpoint. This caused login failures with "invalid credentials" despite valid secrets. The deployment now uses the actual endpoint from `BucketInfo`, with the old namespace-based endpoint kept only as a fallback before `BucketAccess` secrets exist ([**@IvanHunters**](https://github.com/IvanHunters) in #2211, #2215). + +* **[platform] Fix spurious OpenAPI post-processing errors on cozystack-api startup**: The OpenAPI post-processor was being invoked for non-`apps.cozystack.io` group versions where the base `Application*` schemas do not exist, producing noisy startup errors on every API server launch. It now skips those non-apps group versions gracefully instead of returning an error ([**@kvaps**](https://github.com/kvaps) in #2212, #2217). + +## Documentation + +* **[website] Add troubleshooting for packages stuck in `DependenciesNotReady`**: Added an operations guide that explains how to diagnose missing package dependencies in operator logs and corrected the packages management development docs to use the current `make image-packages` target ([**@kvaps**](https://github.com/kvaps) in cozystack/website#450). + +* **[website] Reorder installation docs to install the operator before the platform package**: Updated the platform installation guide and tutorial so the setup sequence consistently installs the Cozystack operator first, then prepares and applies the Platform Package, matching the rest of the documentation set ([**@sircthulhu**](https://github.com/sircthulhu) in cozystack/website#449). + +* **[website] Add automated installation guide for the Ansible collection**: Added a full guide for deploying Cozystack with the `cozystack.installer` collection, including inventory examples, distro-specific playbooks, configuration reference, and explicit version pinning guidance ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#442). + +* **[website] Expand monitoring and platform architecture reference docs**: Added a tenant custom metrics collection guide for `VMServiceScrape` and `VMPodScrape`, and documented `PackageSource`/`Package` architecture, reconciliation flow, rollback behavior, and the `cozypkg` workflow in Key Concepts ([**@IvanHunters**](https://github.com/IvanHunters) in cozystack/website#444, cozystack/website#445). + +* **[website] Improve operations guides for CA rotation and Velero backups**: Completed the CA rotation documentation with dry-run and post-rotation credential retrieval steps, and expanded the backup configuration guide with concrete examples, verification commands, and clearer operator procedures ([**@kvaps**](https://github.com/kvaps) in cozystack/website#406; [**@androndo**](https://github.com/androndo) in cozystack/website#440). + +--- + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.1.1...v1.1.2 diff --git a/docs/changelogs/v1.1.3.md b/docs/changelogs/v1.1.3.md new file mode 100644 index 00000000..f589476e --- /dev/null +++ b/docs/changelogs/v1.1.3.md @@ -0,0 +1,19 @@ + + +## Fixes + +* **[kubernetes] Fix CiliumNetworkPolicy endpointSelector not updated for multi-node RWX volumes**: When an NFS-backed RWX volume was published to multiple VMs, the `CiliumNetworkPolicy` `endpointSelector.matchLabels` was set only for the first VM and never broadened on subsequent `ControllerPublishVolume` calls. This caused Cilium to block NFS egress so that mounts hung on all nodes except the first. The selector now uses `matchExpressions` with `operator: In` and is rebuilt whenever owner references are added or removed ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #2227, #2229). + +* **[dashboard] Fix dashboard-client secret desynchronization with Keycloak after upgrades**: When the `dashboard-client` Kubernetes Secret was recreated with a new value after an upgrade or reinstall, the `KeycloakClient` spec remained unchanged and the EDP Keycloak operator skipped reconciliation, leaving Keycloak with the stale secret and causing authentication failures for the dashboard. A `secret-hash` annotation containing the SHA256 hash of the client secret is now added to the `KeycloakClient` resource; any secret rotation updates the hash in metadata, triggering operator reconciliation and syncing the new secret to Keycloak ([**@sircthulhu**](https://github.com/sircthulhu) in #2231, #2241). + +* **[etcd] Fix defrag CronJob accumulating hundreds of pods during cluster upgrades**: After upgrading CozyStack, the etcd defrag CronJob could accumulate hundreds of running and failed pods when etcd was temporarily unavailable during the upgrade, because no concurrency or retry limits were configured. Added `concurrencyPolicy: Forbid` to prevent parallel jobs, `startingDeadlineSeconds: 300` to discard missed schedules older than 5 minutes, `failedJobsHistoryLimit: 1` to limit failure retention, `activeDeadlineSeconds: 1800` for a 30-minute per-job timeout, and `backoffLimit: 2` to cap retries ([**@sircthulhu**](https://github.com/sircthulhu) in #2233, #2234). + +## Documentation + +* **[website] Document `keycloakInternalUrl` platform value**: Added reference documentation for the `authentication.oidc.keycloakInternalUrl` platform value to the Platform Package Reference, Self-Signed Certificates guide, and Enable OIDC Server guide, explaining how to route dashboard backend OIDC requests through the internal Keycloak service URL ([**@sircthulhu**](https://github.com/sircthulhu) in cozystack/website#452). + +--- + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.1.2...v1.1.3 diff --git a/docs/changelogs/v1.1.4.md b/docs/changelogs/v1.1.4.md new file mode 100644 index 00000000..f1e4ede9 --- /dev/null +++ b/docs/changelogs/v1.1.4.md @@ -0,0 +1,41 @@ + + +## Features and Improvements + +* **[boot-to-talos] Add support for ISO, RAW, and HTTP image sources**: The `boot-to-talos` tool can now use ISO files, raw disk images, and HTTP URLs as Talos image sources in addition to container registry images. This allows bootstrapping nodes in air-gapped environments or from locally stored images without requiring a container registry ([**@lexfrei**](https://github.com/lexfrei) in cozystack/boot-to-talos#13). + +* **[boot-to-talos] Use permanent MAC address for predictable network interface names**: Interface name detection now reads the permanent MAC address directly from sysfs instead of relying on udev data, providing a stable hardware MAC that is unaffected by user modifications to the active MAC address. This makes network interface naming more reliable across reboots and hardware changes ([**@IvanHunters**](https://github.com/IvanHunters) in cozystack/boot-to-talos#14). + +## Fixes + +* **[dashboard] Fix broken backup menu links missing cluster context**: Backup resources (plans, backupjobs, backups) are not `ApplicationDefinition`s, so `ensureNavigation()` never created their `baseFactoriesMapping` entries. Without these entries the OpenUI frontend could not resolve the `{cluster}` context for backup pages, producing broken sidebar links with an empty cluster segment (e.g. `/openapi-ui//tenant-root/...`). The missing `baseFactoriesMapping` entries for all backup resource types are now added to the static `Navigation` resource ([**@sircthulhu**](https://github.com/sircthulhu) in #2232, #2269). + +* **[platform] Fix tenant admins unable to create FoundationDB, Harbor, MongoDB, OpenBAO, OpenSearch, Qdrant, and VPN applications**: The `cozy:tenant:admin:base` `ClusterRole` was missing seven application resources from `apps.cozystack.io` (`foundationdbs`, `harbors`, `mongodbs`, `openbaos`, `opensearches`, `qdrants`, `vpns`). Without these permissions, tenant admins could not create these applications — the "Add" button was inactive in the dashboard. The missing resources have been added to the ClusterRole ([**@sircthulhu**](https://github.com/sircthulhu) in #2268, #2272). + +* **[dashboard] Fix StorageClass dropdown showing "Error" in application forms**: The dashboard UI fetches `StorageClass` resources to populate dropdowns (e.g. in the Postgres form), but the `cozystack-dashboard-readonly` `ClusterRole` did not include `storage.k8s.io/storageclasses`. This caused authenticated users to see "Error" instead of the StorageClass name. `get`/`list`/`watch` permissions for `storageclasses` have been added to the dashboard readonly role ([**@sircthulhu**](https://github.com/sircthulhu) in #2267, #2274). + +* **[system] Fix 403 error on Service details page by granting tenants read access to EndpointSlices**: The dashboard requested `EndpointSlices` from the `discovery.k8s.io` API group to display the "Pod serving" section on the Service details page, but `cozy:tenant:base` and `cozy:tenant:view:base` `ClusterRole`s lacked permissions for this resource. Tenant users received a 403 error when opening the Service details page. `get`/`list`/`watch` permissions for `endpointslices` have been added to both tenant ClusterRoles ([**@sircthulhu**](https://github.com/sircthulhu) in #2257, #2285). + +* **[dashboard] Fix "Pod serving" table displaying "Raw:" and "Invalid Date" on Service details page**: The Service details page `EndpointSlice` table showed "Raw:" prefixes and "Invalid Date" values because the `EnrichedTable` referenced `customizationId` `factory-kube-service-details-endpointslice` which had no corresponding `CustomColumnsOverride`. Column definitions for Pod (`.targetRef.name`), Addresses (`.addresses`), Ready (`.conditions.ready`), and Node (`.nodeName`) have been added ([**@sircthulhu**](https://github.com/sircthulhu) in #2266, #2283). + +* **[piraeus-operator] Fix LINSTOR satellite alert labels, reduce scrape-flap false positives, and improve controller alerting**: Three alerting issues in `cozy-piraeus-operator` have been addressed: (1) `linstorSatelliteErrorRate` used a non-existent `name` label in annotations, resulting in `Satellite ""` in alert notifications — corrected to `{{ $labels.hostname }}`; (2) `linstorSatelliteErrorRate` could produce false positives when the `linstor-controller` scrape flapped and historical `linstor_error_reports_count` counters reappeared inside the alert window — fixed by adding a minimum scrape-count guard; (3) The `LinstorControllerOffline` alert has been split into separate availability and metrics-availability alerts with configurable hold time to reduce noise during brief connectivity interruptions ([**@sasha-sup**](https://github.com/sasha-sup) in #2265, #2286). + +* **[linstor] Fix swapped VMPodScrape job labels causing incorrect controller offline alerts**: The `cozy-linstor` VictoriaMetrics `VMPodScrape` templates had the `job` relabeling rules swapped: `linstor-satellite` metrics were labeled as `job=linstor-controller` and vice versa. This caused `linstorControllerOffline` alerts to fire for satellite endpoints (`:9942`) while reporting that the controller was unreachable. The `job` labels are now correctly assigned to their respective targets ([**@sasha-sup**](https://github.com/sasha-sup) in #2264, #2289). + +* **[boot-to-talos] Fix triple-fault on hosts with 5-level paging (LA57) enabled**: On hosts with `CONFIG_X86_5LEVEL=y` in the kernel, kexec into Talos caused a triple-fault because the Talos kernel does not support 5-level page tables. `boot-to-talos` now detects LA57 before kexec and automatically patches GRUB with `no5lvl`, runs `update-grub`, and reboots. After reboot with 5-level paging disabled, `boot-to-talos` proceeds normally ([**@IvanHunters**](https://github.com/IvanHunters) in cozystack/boot-to-talos#15). + +* **[boot-to-talos] Fix EFI boot entry creation when using loop device images**: Talos installer skips EFI variable creation when running on loop devices. `boot-to-talos` now creates a proper UEFI boot entry with an `HD()` device path pointing to the real target disk's ESP by reading the GPT partition table from the target disk after image copy, instead of relying on the Talos installer ([**@kvaps**](https://github.com/kvaps) in cozystack/boot-to-talos#16). + +* **[talm] Fix silent empty output when no template files are specified**: Running `talm template` without `--file` or `--template` flags previously produced minimal or empty output without any error. Validation has been added to `engine.Render` to return a clear error message when no template files are specified, making misconfigured invocations immediately apparent ([**@kvaps**](https://github.com/kvaps) in cozystack/talm#112). + +## Documentation + +* **[website] Add documentation for VMInstance and VMDisk backups**: Added a new virtualization-focused Backup and Recovery guide covering one-off and scheduled backups for `VMInstance` and `VMDisk` resources, restore procedures, status verification commands, and troubleshooting notes including Velero-related issues ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#456). + +* **[website] Update developer guide with operator-driven architecture and OCIRepository migration flow**: Rewrote the development guide to describe the operator-driven in-cluster architecture, bootstrap flow, operator responsibilities, and the platform install/update sequence. Added an "OCIRepositories and Migration Flow" section with migration hook examples and sequencing rules for pre-upgrade hooks ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#458). + +--- + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.1.3...v1.1.4 diff --git a/docs/changelogs/v1.1.5.md b/docs/changelogs/v1.1.5.md new file mode 100644 index 00000000..e756bdd9 --- /dev/null +++ b/docs/changelogs/v1.1.5.md @@ -0,0 +1,13 @@ + + +## Fixes + +* **[platform] Prevent installed packages deletion**: Added the `helm.sh/resource-policy: keep` annotation to all platform packages. Previously, moving a package to `disabledPackages` or removing it from `enabledPackages` caused Helm to automatically delete it, contradicting the documented behavior that requires the platform administrator to manually delete packages when needed ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2273, #2298). + +* **[linstor] Fix TCP port mismatches after toggle-disk operations causing DRBD resources to enter StandAlone state**: During toggle-disk operations, `removeLayerData()` freed TCP ports from the number pool and `ensureStackDataExists()` could then allocate different ports. If a satellite missed the resulting update (e.g. due to a controller restart), it retained the old ports while peers received the new ones, causing DRBD connections to fail with StandAlone state. The fix introduces `copyDrbdTcpPortsIfExists()`, which preserves existing TCP ports in the `LayerPayload` before `removeLayerData()` releases them ([**@kvaps**](https://github.com/kvaps) in #2292, #2300). + +--- + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.1.4...v1.1.5 diff --git a/docs/changelogs/v1.1.6.md b/docs/changelogs/v1.1.6.md new file mode 100644 index 00000000..77c49475 --- /dev/null +++ b/docs/changelogs/v1.1.6.md @@ -0,0 +1,19 @@ + + +## Fixes + +* **[build] Filter git describe to match only v* tags**: Adds `--match 'v*'` to all `git describe` calls in `hack/common-envs.mk`. The `api/apps/v1alpha1/*` subtags share the same commit as release tags, causing `git describe --exact-match` to pick `api/apps/v1alpha1/vX.Y.Z` instead of `vX.Y.Z`, producing invalid Docker image tags ([**@kvaps**](https://github.com/kvaps) in #2386, #2388). + +## Development, Testing, and CI/CD + +* **[ci] Replace cozystack-bot PAT with cozystack-ci GitHub App**: Replaces the long-lived `cozystack-bot` personal access token with short-lived, scoped tokens from the `cozystack-ci` GitHub App across all release workflows. Improves security and auditability of CI operations ([**@tym83**](https://github.com/tym83) in #2351). + +* **[ci] Replace GH_PAT with cozystack-ci GitHub App token in pull-requests workflow**: Switches the pull-requests release workflow to use the cozystack-ci GitHub App token instead of the personal access token ([**@kvaps**](https://github.com/kvaps) in #2383). + +* **[ci] Use cozystack org noreply email for bot commits**: Updates CI workflows to use the cozystack organization noreply email for bot commits ([**@kvaps**](https://github.com/kvaps) in #2392). + +--- + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.1.5...v1.1.6 diff --git a/docs/changelogs/v1.2.0.md b/docs/changelogs/v1.2.0.md new file mode 100644 index 00000000..7c5b5e24 --- /dev/null +++ b/docs/changelogs/v1.2.0.md @@ -0,0 +1,204 @@ + + +# Cozystack v1.2.0 + +> **⚠️ WARNING: Do not use this release.** This version includes CloudNativePG operator, which updates the default PostgreSQL image to version 18. CNPG is unable to perform the migration from the previous major version automatically, which will cause PostgreSQL clusters to fail to start after the upgrade. Please use [v1.2.1](https://github.com/cozystack/cozystack/releases/tag/v1.2.1) instead. + +Cozystack v1.2.0 delivers significant platform enhancements: a fully managed **OpenSearch** service joining the application catalog, **VPC peering** for secure inter-tenant networking, tenant workload placement control via the new **SchedulingClass** system, a highly-available **VictoriaLogs cluster** replacing the single-node setup, and **Linstor volume relocation** for optimized clone and snapshot restore placement. Additional highlights include external-dns as a standalone extra package, multi-node RWX volume fixes, and a wave of dashboard and monitoring improvements. + +## Feature Highlights + +### OpenSearch: Managed Search and Analytics Service + +Cozystack now ships **OpenSearch** as a fully managed PaaS application — supporting OpenSearch v1, v2, and v3 in a multi-role topology with dedicated master, data, ingest, coordinating, and ML nodes. TLS is enabled by default, HTTP Basic auth is provided out of the box, and custom user definitions allow per-application credentials. The optional **OpenSearch Dashboards** UI can be enabled alongside the engine. External access, topology spread policies, and a comprehensive JSON schema are all included. + +A companion `opensearch-operator` system package wraps the upstream Opster OpenSearch Operator v2.8.0 and adds a sysctl DaemonSet to configure the required `vm.max_map_count` kernel parameter on every node automatically. An ApplicationDefinition package ties everything into the Cozystack platform dashboard with schema validation and resource management. + +### SchedulingClass: Tenant Workload Placement + +Cozystack now supports a **SchedulingClass** CRD that allows platform operators to define cluster-wide scheduling constraints — pinning tenant workloads to specific data centers, hardware generations, or node groups without requiring tenants to manage scheduler configuration themselves. Tenants declare a `schedulingClass` in their Tenant spec; the platform injects the appropriate `schedulerName` into all workloads in that namespace. + +The `lineage-controller-webhook` has been extended to verify the referenced SchedulingClass CR before injection, and child tenants inherit their parent's scheduling constraints (children cannot override). A **SchedulingClass dropdown** in the Tenant creation form in the dashboard makes the feature fully self-service. The underlying `cozystack-scheduler` — a custom kube-scheduler extension with SchedulingClass-aware affinity plugins — is now installed and enabled by default as part of the platform. + +### VPC Peering for Multi-Tenant Environments + +The `vpc` application gains bilateral **VPC peering** using Kube-OVN's native `vpcPeerings` mechanism, allowing tenants to securely interconnect their private networks without routing traffic through public endpoints. Peering link-local IPs (`169.254.0.0/16`) are allocated deterministically from a hash of the sorted VPC pair names, ensuring stable addresses across reconciliations. Static route support (`staticRoutes`) enables fine-grained inter-VPC routing policies. A `cozy-lib` helper (`hexToInt`) performs the deterministic IP allocation, and a JSON Schema validation enforces the `^tenant-` namespace pattern for peered VPCs. + +### VictoriaLogs: Clustered Mode for High Availability + +The platform's log storage has been upgraded from the deprecated single-node `VLogs` CR to a **VLCluster** deployment with separate vlinsert, vlselect, and vlstorage components, each running with 2 replicas by default — consistent with the existing VMCluster setup. This brings horizontal scalability and resilience to the logging tier. VPA autoscaling is enabled for all VLCluster components, and the victoria-metrics-operator has been upgraded from v0.55.0 to v0.68.1 to add VLCluster CRD support. + +### Linstor CSI: Volume Relocation After Clone and Restore + +The Linstor CSI driver now carries upstream patches enabling **automatic replica relocation** after PVC clone and snapshot restore operations. Two new parameters control the behavior: `linstor.csi.linbit.com/relocateAfterClone` on StorageClasses moves replicas to optimal nodes after a clone, and `snap.linstor.csi.linbit.com/relocate-after-restore` on VolumeSnapshotClasses does the same after a restore. VolumeSnapshotClasses for Velero and Kasten use cases are pre-configured. This enables full PVC-level backup and restore workflows with automatic data rebalancing, a key prerequisite for production Velero/Kasten integrations. + +## Major Features and Improvements + +* **[apps] Add managed OpenSearch service**: Deployed as a PaaS application supporting OpenSearch v1/v2/v3 with multi-role node topology, TLS, HTTP Basic auth, custom users, optional OpenSearch Dashboards UI, external access, and topology spread policies; backed by the opster OpenSearch Operator v2.8.0 and a sysctl DaemonSet for `vm.max_map_count` ([**@matthieu-robin**](https://github.com/matthieu-robin) in #1953). + +* **[vpc] Add VPC peering support for multi-tenant environments**: Bilateral VPC peering via Kube-OVN's `vpcPeerings`, deterministic link-local IP allocation from sorted VPC pair hash, static routes support, ConfigMap peer discovery enrichment, and JSON Schema validation enforcing `^tenant-` namespace pattern ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #2152). + +* **[monitoring] Migrate VictoriaLogs from VLogs to VLCluster**: Replaced deprecated single-node `VLogs` CR with clustered `VLCluster` (vlinsert/vlselect/vlstorage, 2 replicas each), added VPA for all components, upgraded victoria-metrics-operator to v0.68.1 ([**@sircthulhu**](https://github.com/sircthulhu) in #2153). + +* **[scheduler] Integrate SchedulingClass support for tenant workloads**: Added `schedulingClass` Tenant parameter with inheritance enforcement, `scheduling.cozystack.io/class` namespace label, lineage-webhook extension to verify and inject `schedulerName`, SchedulingClass dropdown in Tenant dashboard form ([**@sircthulhu**](https://github.com/sircthulhu) in #2223). + +* **[cozystack-scheduler] Add custom scheduler as an optional system package**: Vendored `cozystack-scheduler` from github.com/cozystack/cozystack-scheduler — a kube-scheduler extension with SchedulingClass-aware affinity plugins, including Helm chart with RBAC, ConfigMap, Deployment, and CRD ([**@lllamnyp**](https://github.com/lllamnyp) in #2205). + +* **[platform] Enable cozystack-scheduler by default**: The cozystack-scheduler and SchedulingClass CRD are now installed as default system packages; the backup tool has been moved to optional packages ([**@lllamnyp**](https://github.com/lllamnyp) in #2253). + +* **[extra] Add external-dns as a standalone extra package**: Packaged external-dns as an installable extra (tenant-level) component for automatic DNS record management from Kubernetes Service and Ingress resources ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #1988). + +* **[linstor] Add linstor-csi patches for clone/snapshot relocation**: New patch enabling `relocateAfterClone` StorageClass parameter and `relocate-after-restore` VolumeSnapshotClass parameter; pre-configured VolumeSnapshotClasses for Velero and relocation workflows; CDI switched to csi-clone strategy ([**@kvaps**](https://github.com/kvaps) in #2133). + +* **[monitoring] Add inlineScrapeConfig support to tenant vmagent**: Tenants can now define inline scrape configurations directly in their VMAgent spec, enabling custom metrics collection from services that are not discoverable via standard Kubernetes service discovery ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #2200). + +* **[monitoring] Add Slack dashboard URL, vmagent environment label, and dynamictext Grafana plugin**: Added `SLACK_DASHBOARD_URL` and `SLACK_SUMMARY_FMT` environment variables for richer alert notifications, per-vmagent `environment` label for metric source identification, and the `dynamictext-panel` plugin for Grafana dashboards ([**@vnyakas**](https://github.com/vnyakas) in #2210). + +* **[monitoring] Scope infrastructure dashboards to tenant-root only**: Infrastructure-level Grafana dashboards are now scoped to the tenant-root namespace only, preventing them from appearing in tenant sub-namespaces and reducing dashboard noise ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #2197). + +* **[tenant] Allow egress to virt-handler for VM metrics scraping**: Extended tenant NetworkPolicy to permit egress to virt-handler pods, enabling Prometheus to scrape VM-level metrics from KubeVirt without additional policy exceptions ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #2199). + +* **[dashboard] Add keycloakInternalUrl for backend-to-backend OIDC requests**: Added a `keycloakInternalUrl` platform value for the dashboard backend to perform OIDC token introspection via an internal cluster URL, avoiding external round-trips and improving reliability in air-gapped environments ([**@sircthulhu**](https://github.com/sircthulhu) in #2224). + +* **[dashboard] Add secret-hash annotation to KeycloakClient for secret sync**: Added a `secret-hash` annotation to the KeycloakClient resource so that changes to the client secret trigger automatic reconciliation and propagation to dependent components ([**@sircthulhu**](https://github.com/sircthulhu) in #2231). + +* **[docs] Add OpenAPI and Go types code generation for apps**: Added tooling to generate OpenAPI schemas and Go types from Helm chart values, enabling type-safe programmatic access to managed application configurations and automatic API reference generation ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2214). + +## Improvements (minor) + +* **[cozystack-scheduler] Update to v0.2.0**: Updated the cozystack-scheduler to v0.2.0 with improved SchedulingClass affinity handling ([**@lllamnyp**](https://github.com/lllamnyp) in #2244). + +* **[platform] Ensure cozystack-packages OCIRepository updates reliably**: Added configuration to ensure the `cozystack-packages` OCIRepository resource is consistently reconciled and reflects the latest package versions on upgrade ([**@sircthulhu**](https://github.com/sircthulhu) in #2246). + +* **[etcd] Add protective limits to defrag CronJob**: Added CPU and memory resource limits to the etcd defragmentation CronJob to prevent it from starving other workloads during scheduled defragmentation runs ([**@sircthulhu**](https://github.com/sircthulhu) in #2233). + +* **[platform] Add missing apps to tenant admin RBAC**: Extended the tenant admin ClusterRole to include RBAC permissions for recently added applications that were missing from the role binding ([**@sircthulhu**](https://github.com/sircthulhu) in #2268). + +## Bug Fixes + +* **[keycloak] Fix health probe configuration for Keycloak v26.x+**: Replaced deprecated `KC_PROXY=edge` with `KC_PROXY_HEADERS=xforwarded`/`KC_HTTP_ENABLED=true`; replaced liveness/readiness probes with management port endpoints (`/health/live`, `/health/ready`) and added a `startupProbe` to handle slow Keycloak startup without triggering premature restarts ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #2162). + +* **[migrations] Handle missing RabbitMQ CRD in migration 34**: Fixed a crash in migration script 34 that occurred when the RabbitMQ CRD was not yet installed, allowing upgrades from environments where RabbitMQ was never deployed ([**@IvanHunters**](https://github.com/IvanHunters) in #2168). + +* **[platform] Fix VM MAC address not preserved during migration**: Fixed the `virtual-machine` to `vm-instance` migration script to correctly carry over the MAC address, preventing network identity changes after upgrading existing VM resources ([**@sircthulhu**](https://github.com/sircthulhu) in #2169). + +* **[dashboard] Fix External IPs factory EnrichedTable rendering**: Corrected the External IPs factory component to use the EnrichedTable renderer, resolving blank/broken rendering of the external IP address list in the dashboard ([**@IvanHunters**](https://github.com/IvanHunters) in #2175). + +* **[dashboard] Preserve disabled/hidden state on MarketplacePanel reconciliation**: Fixed a regression where MarketplacePanel reconciliation would reset the `disabled` and `hidden` flags set by operators, causing hidden applications to reappear in the catalog ([**@IvanHunters**](https://github.com/IvanHunters) in #2176). + +* **[dashboard] Exclude hidden MarketplacePanel resources from sidebar menu**: Fixed the sidebar to omit applications that have been hidden via MarketplacePanel flags, preventing inaccessible menu entries from being displayed to users ([**@IvanHunters**](https://github.com/IvanHunters) in #2177). + +* **[etcd-operator] Replace deprecated kube-rbac-proxy image**: Replaced the unmaintained `gcr.io/kubebuilder/kube-rbac-proxy` sidecar with the actively maintained `quay.io/brancz/kube-rbac-proxy` image to eliminate deprecation warnings and ensure continued security updates ([**@kvaps**](https://github.com/kvaps) in #2181). + +* **[backups] Fix RBAC and backupstrategy-controller location**: Corrected role bindings and the deployment location for the backup strategy controller to restore full backup and restore functionality ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2149). + +* **[api] Skip OpenAPI post-processor for non-apps group versions**: Fixed the API server to bypass OpenAPI schema post-processing for non-`apps` group versions, preventing schema corruption in unrelated API groups ([**@kvaps**](https://github.com/kvaps) in #2212). + +* **[bucket] Fix s3manager endpoint mismatch with COSI credentials**: Corrected the S3 Manager UI to use the actual S3 endpoint from the BucketInfo COSI resource rather than a hardcoded value, resolving connection failures when the S3 endpoint differs from the default ([**@IvanHunters**](https://github.com/IvanHunters) in #2211). + +* **[kubernetes] Fix tenant Kubernetes cluster creation for versions < 1.32**: Resolved a template rendering error that prevented creation of tenant Kubernetes clusters with versions older than 1.32 ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2209). + +* **[kube-ovn] Fix MASTER_NODES detection for multi-master Kubernetes clusters**: Updated kube-ovn configuration to discover control-plane nodes via the standard `node-role.kubernetes.io/control-plane` label rather than relying on static node lists, fixing OVN connectivity issues in multi-master generic Kubernetes deployments ([**@lexfrei**](https://github.com/lexfrei) in #2245). + +* **[kubernetes] Fix CiliumNetworkPolicy endpointSelector for multi-node RWX volumes**: Corrected the CiliumNetworkPolicy endpoint selector for NFS-based ReadWriteMany volumes to properly allow NFS traffic when data is spread across multiple Linstor storage nodes ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #2227). + +* **[csi] Hide disk.img and lost+found from RWX NFS mounts**: Fixed the Linstor CSI NFS server to exclude internal files (`disk.img`, `lost+found`) from being visible inside NFS-mounted volumes, preventing application errors caused by unexpected files in volume root directories ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #2243). + +* **[dashboard] Fix broken backup menu links missing cluster context**: Restored cluster context in backup-related sidebar navigation links, fixing 404 errors when navigating to BackupJob and Plan pages from the cluster-level dashboard view ([**@kvaps**](https://github.com/kvaps) in #2232). + +* **[dashboard] Fix StorageClass dropdown "Error" state by granting RBAC read access**: Added a ClusterRole/ClusterRoleBinding to grant authenticated users read access to StorageClass resources, resolving the "Error" state displayed in StorageClass dropdowns on application forms ([**@sircthulhu**](https://github.com/sircthulhu) in #2267). + +* **[postgres] Fix database deletion lifecycle management**: Added cleanup stages to delete databases and orphaned roles when removed from `values.databases`, enabling declarative database lifecycle management and preventing stale data retention ([**@sircthulhu**](https://github.com/sircthulhu) in #2247). + +* **[dashboard] Fix JSONPath crash on Tenant details with resourceQuotas**: Restored fallback protection for unresolved flatMap placeholders in the ResourceQuota "Used" column, preventing JSONPath parser crashes on the Tenant details page ([**@sircthulhu**](https://github.com/sircthulhu) in #2249). + +* **[system] Fix tenant RBAC for endpointslices read access**: Added `discovery.k8s.io/endpointslices` read permissions to tenant ClusterRoles, resolving 403 errors on the Service details page when displaying the "Pod serving" section ([**@sircthulhu**](https://github.com/sircthulhu) in #2257). + +* **[linstor] Fix swapped VMPodScrape job labels**: Corrected the `job` label relabeling in LINSTOR VictoriaMetrics PodScrape templates, fixing `linstorControllerOffline` alerts that incorrectly reported satellite endpoints as controller failures ([**@sasha-sup**](https://github.com/sasha-sup) in #2264). + +* **[piraeus-operator] Fix LINSTOR satellite alert labels and scrape flapping false positives**: Fixed non-existent `name` label in `linstorSatelliteErrorRate` alert annotations (changed to `hostname`) and prevented false positives caused by scrape flapping and stale metric counters ([**@sasha-sup**](https://github.com/sasha-sup) in #2265). + +* **[dashboard] Fix EndpointSlice column definitions for Pod serving table**: Added missing `CustomColumnsOverride` for the EndpointSlice table on service details page, replacing "Raw:" prefixes and "Invalid Date" values with proper Pod, Addresses, Ready, and Node columns ([**@sircthulhu**](https://github.com/sircthulhu) in #2266). + +## Dependencies & Version Updates + +* **[cilium] Update Cilium to v1.19.1**: Upgraded the Cilium CNI to v1.19.1 with latest bug fixes and performance improvements ([**@BROngineer**](https://github.com/BROngineer) in #2173). + +* **[keycloak-operator] Update to v1.32.0**: Updated the Keycloak Operator to v1.32.0 (based on epam/edp-keycloak-operator with upstream patches), bumping Keycloak to 26.5.2 ([**@lllamnyp**](https://github.com/lllamnyp) in #2206). + +* **[postgres-operator] Update to v1.27.3**: Upgraded the Postgres Operator (Patroni-based) to v1.27.3 with latest upstream fixes ([**@dmpopoff**](https://github.com/dmpopoff) in #2226). + +* **[objectstorage-controller] Update to v0.2.2, drop upstreamed patches**: Updated the object storage controller to v0.2.2 and removed patches that were accepted upstream, reducing the maintenance delta ([**@lexfrei**](https://github.com/lexfrei) in #2261). + +* **[kilo] Switch from fork to upstream squat/kilo**: Replaced the Cozystack-maintained Kilo fork with the upstream `squat/kilo` image now that required patches (`--internal-cidr`, allowed-location-ips fix, preferred source for WireGuard routes, Cilium IPIP overlay support) have been merged upstream ([**@lexfrei**](https://github.com/lexfrei) in #2259). + +* **[talos] Bump Talos to v1.12.6**: Updated the pinned Talos version to v1.12.6 ([**@kvaps**](https://github.com/kvaps) in #2254). + +* **[talm] Release v0.22.4** (github.com/cozystack/talm): Fixed `--file`/`--template` flag requirement to prevent ambiguous invocations ([**@kvaps**](https://github.com/kvaps) in cozystack/talm#112). + +* **[boot-to-talos] Release v0.7.0** (github.com/cozystack/boot-to-talos): Added support for ISO, RAW, and HTTP image sources ([**@lexfrei**](https://github.com/lexfrei) in cozystack/boot-to-talos#13); permanent MAC addresses for predictable interface names ([**@IvanHunters**](https://github.com/IvanHunters) in cozystack/boot-to-talos#14); detection and workaround for 5-level paging (LA57) incompatibility with kexec ([**@IvanHunters**](https://github.com/IvanHunters) in cozystack/boot-to-talos#15). + +* **[boot-to-talos] Release v0.7.1** (github.com/cozystack/boot-to-talos): Fixed EFI boot entry creation to use the target disk rather than relying on the installer disk, preventing boot failures on bare-metal systems ([**@kvaps**](https://github.com/kvaps) in cozystack/boot-to-talos#16). + +## Development, Testing, and CI/CD + +* **[tests] Stabilize E2E Kubernetes tests**: Comprehensive improvements to E2E test stability: pre-cleanup of leftover resources, fixes for port-forward race conditions and leaks, improved NFS PVC timeout and debug output, proper EXIT trap handling, and increased CAPI deployment timeouts ([**@lexfrei**](https://github.com/lexfrei) in #2262). + +* **[ci] Fix E2E check blocking docs-only PRs**: Moved path filtering to the job level so that documentation-only pull requests are not blocked by pending E2E CI checks ([**@IvanHunters**](https://github.com/IvanHunters) in #2170). + +* **[ci] Add timeout-minutes to Build and E2E jobs**: Added explicit `timeout-minutes` constraints to Build and E2E workflow jobs to prevent stuck runners from consuming CI resources indefinitely. + +## Documentation + +* **[website] Complete CA rotation documentation**: Added comprehensive CA certificate rotation procedures for all Cozystack system components ([**@kvaps**](https://github.com/kvaps) in cozystack/website#406). + +* **[website] Add Ansible automated installation guide**: Added a step-by-step guide for automated Cozystack installation using Ansible playbooks ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#442). + +* **[website] Add self-signed certificates configuration guide for OIDC**: Added documentation for configuring Cozystack to use self-signed TLS certificates with OIDC providers, covering certificate authority setup and kubeconfig integration ([**@IvanHunters**](https://github.com/IvanHunters) in cozystack/website#443). + +* **[website] Add custom metrics collection guide**: Added a guide explaining how to configure custom Prometheus scrape targets using the new `inlineScrapeConfig` feature of tenant VMAgent ([**@IvanHunters**](https://github.com/IvanHunters) in cozystack/website#444). + +* **[website] Add PackageSource/Package architecture to Key Concepts**: Documented the PackageSource and Package CRD architecture, explaining how operators extend the platform with custom application catalogs ([**@IvanHunters**](https://github.com/IvanHunters) in cozystack/website#445). + +* **[website] Add SchedulingClass operations guide**: Added a guide covering SchedulingClass CRD creation, tenant assignment, and workload placement verification ([**@lllamnyp**](https://github.com/lllamnyp) in cozystack/website#455). + +* **[website] Add VMInstance and VMDisk backups documentation**: Added user-facing documentation for backing up and restoring virtual machine instances and VM disk images using Velero ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#456). + +* **[website] Update developer guide**: Updated the developer guide with current build, test, and contribution workflows including OCIRepository and migration tooling ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#458). + +* **[website] Document keycloakInternalUrl platform value**: Added documentation explaining how to configure `keycloakInternalUrl` for backend-to-backend OIDC token introspection in cluster-internal environments ([**@sircthulhu**](https://github.com/sircthulhu) in cozystack/website#452). + +* **[website] Add DependenciesNotReady troubleshooting guide**: Added a troubleshooting article explaining how to diagnose and resolve the `DependenciesNotReady` package status condition ([**@kvaps**](https://github.com/kvaps) in cozystack/website#450). + +* **[website] Reorder installation steps for operator-before-platform**: Updated the installation guide to install the cozystack-operator before applying the platform package, reflecting the correct dependency order ([**@sircthulhu**](https://github.com/sircthulhu) in cozystack/website#449). + +* **[website] Update managed apps reference**: Updated the automatically generated managed applications reference documentation to reflect new apps and schema changes in this release ([**@app/github-actions**](https://github.com/apps/github-actions) in cozystack/website#448). + +* **[website] Update screenshots for Cozystack v1.1**: Refreshed dashboard screenshots to reflect the updated UI in Cozystack v1.1 ([**@kvaps**](https://github.com/kvaps) in cozystack/website#447). + +* **[website] Enhance operator backups guide**: Improved the backup and recovery guide for operators with additional recovery scenarios and procedures ([**@androndo**](https://github.com/androndo) in cozystack/website#440). + +## Contributors + +We'd like to thank all contributors who made this release possible: + +* [**@androndo**](https://github.com/androndo) +* [**@BROngineer**](https://github.com/BROngineer) +* [**@dmpopoff**](https://github.com/dmpopoff) +* [**@IvanHunters**](https://github.com/IvanHunters) +* [**@kvaps**](https://github.com/kvaps) +* [**@lexfrei**](https://github.com/lexfrei) +* [**@lllamnyp**](https://github.com/lllamnyp) +* [**@mattia-eleuteri**](https://github.com/mattia-eleuteri) +* [**@matthieu-robin**](https://github.com/matthieu-robin) +* [**@myasnikovdaniil**](https://github.com/myasnikovdaniil) +* [**@sasha-sup**](https://github.com/sasha-sup) +* [**@sircthulhu**](https://github.com/sircthulhu) +* [**@tym83**](https://github.com/tym83) +* [**@vnyakas**](https://github.com/vnyakas) + +--- + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.1.0...v1.2.0 diff --git a/docs/changelogs/v1.2.1.md b/docs/changelogs/v1.2.1.md new file mode 100644 index 00000000..29e3fdf6 --- /dev/null +++ b/docs/changelogs/v1.2.1.md @@ -0,0 +1,31 @@ + + +## Features and Improvements + +* **[postgres] Hardcode PostgreSQL 17 for monitoring databases and add migration**: CloudNativePG operator defaults to PostgreSQL 18.3 when no explicit image is specified, but monitoring queries in Grafana and Alerta rely on PostgreSQL 17 features such as `pg_stat_checkpointer` and the updated `pg_stat_bgwriter`. This mismatch could break monitoring after fresh installs or database recreation. PostgreSQL 17.7 images are now hardcoded for monitoring databases, and migration 37 is added to set version v17 for any existing PostgreSQL resources ([**@IvanHunters**](https://github.com/IvanHunters) in #2304, #2309). + +## Fixes + +* **[platform] Prevent installed packages deletion**: Added the `helm.sh/resource-policy: keep` annotation to all platform packages. Previously, moving a package to `disabledPackages` or removing it from `enabledPackages` caused Helm to automatically delete the corresponding resource, contradicting the documented behavior that requires the platform administrator to manually delete packages when needed ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2273, #2297). + +* **[linstor] Preserve TCP ports during toggle-disk operations**: During toggle-disk operations, `removeLayerData()` freed TCP ports from the number pool and `ensureStackDataExists()` could then allocate different ports. If a satellite missed the resulting update (e.g. due to a controller restart), it retained the old ports while peers received the new ones, causing DRBD connections to fail with StandAlone state. The fix adds `copyDrbdTcpPortsIfExists()` which saves existing TCP ports into the `LayerPayload` before `removeLayerData()` deletes them ([**@kvaps**](https://github.com/kvaps) in #2292, #2299). + +* **[platform] Fix resource allocation ratios not propagated to managed packages**: A regression introduced in the bundle restructure caused `cpuAllocationRatio`, `memoryAllocationRatio`, and `ephemeralStorageAllocationRatio` set in `platform/values.yaml` to become no-ops — they were never written to the `cozystack-values` Secret that cozy-lib reads in child packages. This meant all managed applications silently used the hardcoded defaults (10, 1, 40) regardless of operator-configured values. The fix restores propagation by writing the ratios into the `_cluster` section of the `cozystack-values` Secret and passing `cpuAllocationRatio` to the KubeVirt Package component ([**@sircthulhu**](https://github.com/sircthulhu) in #2296, #2301). + +* **[linstor] Fix DRBD connectivity failures on kernels without `crct10dif` by setting verify-alg to `crc32c`**: LINSTOR's auto-verify algorithm selection defaults to `crct10dif`, but this kernel crypto module is no longer available in newer kernels (e.g. Talos v1.12.6, kernel 6.18.18). When `crct10dif` is unavailable, DRBD peer connections fail with `VERIFYAlgNotAvail: failed to allocate crct10dif for verify`, causing all DRBD resources to enter Diskless state and lose quorum. `DrbdOptions/Net/verify-alg` is now set to `crc32c` at the controller level ([**@kvaps**](https://github.com/kvaps) in #2303, #2312). + +* **[multus] Fix stale sandbox reservations permanently blocking pod creation after CNI ADD failure**: After a node disruption (e.g. DRBD or kube-ovn issues during upgrade), containerd accumulated stale sandbox name reservations. Cleanup failed because multus called delegate plugins for DEL without cached state and they rejected the incomplete config, causing DEL to fail instead of succeeding. Stale entries were never released, permanently blocking new pod creation on the affected node. A custom multus-cni image is now built with a patch that returns success from DEL when CNI ADD never completed ([**@kvaps**](https://github.com/kvaps) in #2313, #2314). + +* **[multus] Pin master CNI to `05-cilium.conflist` to prevent race condition at boot**: During node boot or Talos upgrade, multus auto-detects the master CNI conflist by scanning the CNI config directory. If kube-ovn writes `10-kube-ovn.conflist` before Cilium writes `05-cilium.conflist`, multus selects the wrong file and pods bypass the Cilium chain entirely, have no Cilium endpoint, and their traffic is blocked by cluster-wide network policies. `multusMasterCNI` is now pinned to `05-cilium.conflist` ([**@kvaps**](https://github.com/kvaps) in #2315, #2316). + +## Documentation + +* **[website] Add custom Keycloak themes documentation**: Added documentation for custom Keycloak theme injection to the White Labeling guide, covering the theme image contract (`/themes/` directory structure), configuration via the `cozystack.keycloak` Package resource, `imagePullSecrets` for private registries, and theme activation in the Keycloak admin console ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#463). + +* **[website] Add documentation for Go types usage**: Added a guide for using the generated Go types for Cozystack managed applications as a Go module, including installation instructions, programmatic resource management examples, and deployment approaches ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#465). + +--- + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.2.0...v1.2.1 diff --git a/docs/changelogs/v1.2.2.md b/docs/changelogs/v1.2.2.md new file mode 100644 index 00000000..2b6a1ae5 --- /dev/null +++ b/docs/changelogs/v1.2.2.md @@ -0,0 +1,63 @@ + + +## Features and Improvements + +* **[linstor] Update piraeus-server to v1.33.2 with selected backports**: Bumps LINSTOR server from v1.33.1 to v1.33.2 and adds backported patches for improved storage reliability: a stale bitmap adjust retry mechanism for automatic recovery after bitmap attach errors, LUKS2 header sizing and optimal I/O size detection improvements for more reliable disk formatting, and the maintainer implementation backport. All patches verified against upstream v1.33.2 with `git apply --check` and `gradlew compileJava` ([**@kvaps**](https://github.com/kvaps) in #2331, #2377). + +## Fixes + +* **[postgres] Fix system PostgreSQL images to 17.7-standard-trixie**: Hardcodes PostgreSQL 17.7-standard-trixie images for system PostgreSQL instances. This ensures system databases use the correct image variant consistent with the monitoring stack requirements introduced in v1.2.1 ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2364, #2369). + +* **[cilium] Opt-out of cri-containerd.apparmor.d for nsenter init containers**: On Ubuntu 22.04+, Debian, and other distributions that load the `cri-containerd.apparmor.d` AppArmor profile by default for containerd workloads, the kernel denied `nsenter` namespace entry in cilium-agent init containers (`mount-cgroup`, `apply-sysctl-overwrites`, `clean-cilium-state`), causing the agent to land in `Init:CrashLoopBackOff` and cascading platform failures. Per-container `container.apparmor.security.beta.kubernetes.io` annotations now opt the affected containers out of this profile, applied only on non-Talos cilium variants (`cilium-generic`, `kubeovn-cilium-generic`). The vendored daemonset template is also patched to strip the upstream `semverCompare "<1.30.0"` AppArmor block, preventing duplicate annotation keys. Talos variants are untouched as Talos does not load the AppArmor LSM ([**@lexfrei**](https://github.com/lexfrei) in #2370, #2378). + +* **[virtual-machine] Exclude external VM services from Cilium BPF LB**: Adds the `service.kubernetes.io/service-proxy-name: "cozy-proxy"` label to VM LoadBalancer services when `external: true`, telling Cilium to skip BPF processing entirely for these services. This fixes two issues: inter-tenant connectivity via public LB IPs (Cilium's DNAT caused cross-tenant pod-to-pod flow classification, triggering CiliumClusterwideNetworkPolicy blocks) and WholeIP broken on Cilium 1.19+ (wildcard service drop entries blocked traffic to LB IPs on undeclared ports before it reached netfilter/cozy-proxy). MetalLB L2 advertisement and kube-ovn routing remain unaffected ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #2357, #2361). + +* **[monitoring] Fix infra dashboards missing in default variant**: The default platform variant deploys the monitoring chart to the `cozy-monitoring` namespace, but the dashboard rendering condition introduced in #2197 only checked for `tenant-root`. Infrastructure dashboards were not rendered in the default variant. The `cozy-monitoring` namespace is now included in the rendering condition, consistent with the existing pattern in `vmagent.yaml` ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #2365, #2367). + +* **[build] Filter git describe to match only v* tags**: Adds `--match 'v*'` to all `git describe` calls in `hack/common-envs.mk`. The `api/apps/v1alpha1/*` subtags share the same commit as release tags, causing `git describe --exact-match` to pick `api/apps/v1alpha1/vX.Y.Z` instead of `vX.Y.Z`, producing invalid Docker image tags ([**@kvaps**](https://github.com/kvaps) in #2386, #2389). + +## Development, Testing, and CI/CD + +* **[ci] Replace cozystack-bot PAT with cozystack-ci GitHub App**: Replaces the long-lived `cozystack-bot` personal access token with short-lived, scoped tokens from the `cozystack-ci` GitHub App across all release workflows (`tags.yaml`, `auto-release.yaml`, `pull-requests-release.yaml`). Improves security and auditability of CI operations ([**@tym83**](https://github.com/tym83) in #2351). + +* **[ci] Use cozystack org noreply email for bot commits**: Updates CI workflows to use the cozystack organization noreply email for bot commits ([**@kvaps**](https://github.com/kvaps) in #2392, #2393). + +* **[ci] Replace GH_PAT with cozystack-ci GitHub App token in pull-requests workflow**: Switches the pull-requests release workflow to use the cozystack-ci GitHub App token instead of the personal access token ([**@kvaps**](https://github.com/kvaps) in #2383, #2384). + +## Documentation + +* **[website] Add ApplicationDefinition naming convention reference**: Added reference documentation on ApplicationDefinition naming conventions and how `cozystack-api` resolves kinds to their backing definitions ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#478). + +* **[website] Document Talos / talosctl / Cozystack version pairing**: Added documentation covering Talos, talosctl, and Cozystack version compatibility matrix for installation ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#484). + +* **[website] Fix KubeOVN MASTER_NODES example path and key in troubleshooting**: Corrected the MASTER_NODES example path and key in the KubeOVN troubleshooting guide ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#483). + +* **[website] Prefix bundle package names with cozystack. in v1 examples**: Updated documentation examples to use the correct `cozystack.` prefix for bundle package names in enabled/disabledPackages ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#482). + +* **[website] Finish isolated-field removal and document opt-in policy labels**: Removed the obsolete `isolated` field from tenant documentation and documented the new opt-in policy labels approach ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#481). + +* **[website] Add --take-ownership flag and describe networking.* fields**: Added documentation for the `--take-ownership` flag and described the `networking.*` fields in the installation guide ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#480). + +* **[website] Add bonding (LACP) configuration how-to guide**: Added a guide for configuring network bonding with LACP on Cozystack installations ([**@sircthulhu**](https://github.com/sircthulhu) in cozystack/website#459). + +* **[website] Improve registry mirrors for tenant Kubernetes in air-gapped guide**: Improved documentation for configuring registry mirrors in tenant Kubernetes clusters for air-gapped environments ([**@sircthulhu**](https://github.com/sircthulhu) in cozystack/website#461). + +* **[website] Update backup/restore documentation for VMI/VMDisk**: Updated backup documentation with information related to VM instance and VM disk restore improvements ([**@androndo**](https://github.com/androndo) in cozystack/website#466). + +* **[website] Add updated OpenAPI spec**: Updated the OpenAPI specification for managed applications reference ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#469). + +* **[website] Add OSS Health pages and OpenSSF badge**: Added OSS Health section with OpenSSF Scorecard and Best Practices badge to the website footer ([**@tym83**](https://github.com/tym83) in cozystack/website#470). + +* **[website] Add CozySummit Virtual 2026 program announcement**: Published the CozySummit Virtual 2026 program announcement blog post ([**@tym83**](https://github.com/tym83) in cozystack/website#472). + +* **[website] Add missing release announcements for v0.1–v0.41**: Backfilled missing release announcement blog posts for Cozystack versions v0.1 through v0.41 ([**@tym83**](https://github.com/tym83) in cozystack/website#468). + +* **[talm] Render templates online in apply to resolve lookups**: Fixed talm `apply` command to render templates online, resolving template lookup failures when using modeline templates ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/talm#119). + +* **[talm] Update default Talos image to v1.12.6**: Updated the default Talos image version to v1.12.6 in talm ([**@kvaps**](https://github.com/kvaps) in cozystack/talm@03e9b6e). + +--- + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.2.1...v1.2.2 diff --git a/docs/changelogs/v1.2.3.md b/docs/changelogs/v1.2.3.md new file mode 100644 index 00000000..6d1b3b74 --- /dev/null +++ b/docs/changelogs/v1.2.3.md @@ -0,0 +1,58 @@ + + +# v1.2.3 (2026-04-20) + +A patch release with bug fixes and documentation updates. + +## Features and Improvements + +_No notable features in this patch release._ + +## Fixes + +* **fix(kubernetes): set explicit ephemeral-storage on virt-launcher pods**: Prevents VM crashes caused by ephemeral-storage eviction by setting explicit `domain.resources` ephemeral-storage on the VirtualMachine spec. Uses sanitized limits and requests so virt-launcher pods do not inherit too-small namespace defaults. ([**@kvaps**](https://github.com/kvaps) in #2317, backport #2423). + +## Documentation + +* **[website] feat: add Telemetry page under OSS Health section**: Add Telemetry page and initial data seeding to OSS Health docs ([**@tym83**](https://github.com/tym83) in cozystack/website#471). +* **[website] Refactor docs versions to major.minor variants**: Move docs to major.minor versioning for v1.x series ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#477). +* **[website] docs(tenants): document namespace layout and parent/child derivation** ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#479). +* **[website] docs(tenants): document the checkbox-then-edit-CR customization pattern** ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#485). +* **[website] docs: fix 14 broken links and stale talm anchor across v1 docs** ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#486). +* **[website] fix(og): update social badge image and title** ([**@tym83**](https://github.com/tym83) in cozystack/website#487). +* **[website] docs(external-apps): rewrite guide for ApplicationDefinition API** ([**@kitsunoff**](https://github.com/kitsunoff) in cozystack/website#488). +* **[website] docs: add CLAUDE.md for AI agent guidance** ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#489). +* **[website] fix: update /docs/v1/ redirect to latest v1.2** ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#492). +* **[website] fix(ci): add OpenAPI spec download to GitHub Pages build** ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#494). +* **[website] feat(blog): add managed PostgreSQL with synchronous replication post** ([**@tym83**](https://github.com/tym83) in cozystack/website#497). +* **[website] chore(blog): add images frontmatter for social preview on existing posts** ([**@tym83**](https://github.com/tym83) in cozystack/website#498). +* **[website] feat(blog): taxonomies and client-side filter UI** ([**@tym83**](https://github.com/tym83) in cozystack/website#499). +* **[website] style(oss-health): add breathing room between navbar and hero** ([**@tym83**](https://github.com/tym83) in cozystack/website#500). + +## Other repositories + +* **[talm] feat(config): migrate to Talos v1.12 multi-document config format**: Upgrade Talos config format and modernize configuration handling ([**@lexfrei**](https://github.com/lexfrei) in cozystack/talm#116). +* **[talm] chore(deps): bump dependencies and modernize codebase** ([**@lexfrei**](https://github.com/lexfrei) in cozystack/talm#124). +* **[external-apps-example] feat: replace MongoDB example with Minecraft apps from cozylex** ([**@lexfrei**](https://github.com/lexfrei) in cozystack/external-apps-example#2). +* **[ansible-cozystack] fix(examples): add v prefix to collection version in requirements.yml** ([**@lexfrei**](https://github.com/lexfrei) in cozystack/ansible-cozystack#23). +* **[ansible-cozystack] fix(plugins): replace ansible.utils.ipaddr with stdlib-based test plugin** ([**@lexfrei**](https://github.com/lexfrei) in cozystack/ansible-cozystack#24). +* **[ansible-cozystack] feat(examples): comprehensive node prerequisites audit (fixes #19)** ([**@lexfrei**](https://github.com/lexfrei) in cozystack/ansible-cozystack#27). +* **[ansible-cozystack] chore(deps): update dependency cozystack.installer to v1.2.3** ([**@app/renovate**](https://github.com/apps/renovate) in cozystack/ansible-cozystack#29). +* **[ansible-cozystack] feat(role): expose publishing.externalIPs and tenant-root ingress via role variables** ([**@lexfrei**](https://github.com/lexfrei) in cozystack/ansible-cozystack#30). + +## Contributors + +Thanks to everyone who contributed to this patch release: + +* [**@app/github-actions**](https://github.com/apps/github-actions) +* [**@app/renovate**](https://github.com/apps/renovate) +* [**@kitsunoff**](https://github.com/kitsunoff) +* [**@kvaps**](https://github.com/kvaps) +* [**@lexfrei**](https://github.com/lexfrei) +* [**@myasnikovdaniil**](https://github.com/myasnikovdaniil) +* [**@tym83**](https://github.com/tym83) + + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.2.2...v1.2.3 diff --git a/docs/changelogs/v1.3.0-rc.1.md b/docs/changelogs/v1.3.0-rc.1.md new file mode 100644 index 00000000..9b63c106 --- /dev/null +++ b/docs/changelogs/v1.3.0-rc.1.md @@ -0,0 +1,173 @@ + + +# Cozystack v1.3.0-rc.1 + +Cozystack v1.3.0-rc.1 is the first release candidate for v1.3.0, bringing **storage-aware scheduling** via the LINSTOR scheduler extender, a managed **LINSTOR GUI** web UI with Keycloak SSO, a **VM Default Images** catalog for out-of-the-box virtual machine provisioning, **WorkloadsReady conditions** with a real-time Events tab in the dashboard, and **cross-namespace VM backup restore** capabilities. Additional highlights include stricter tenant name validation, VM network selector improvements, Keycloak theme injection and SMTP configuration, and a comprehensive host runtime preflight check. + +> **Note:** Fixes marked with *(backported to v1.2.x)* were also included in v1.2.1 or v1.2.2 patch releases. + +## Feature Highlights + +### Storage-Aware Scheduling via LINSTOR Extender + +The `cozystack-scheduler` now calls the **LINSTOR scheduler extender** for storage-locality-aware pod placement. When a pod declares both a `SchedulingClass` and LINSTOR-backed PVCs, the scheduler consults LINSTOR to prefer nodes where volume replicas already exist — reducing cross-node replication traffic and improving I/O latency for storage-heavy workloads ([**@lllamnyp**](https://github.com/lllamnyp) in #2330). + +### LINSTOR GUI: Managed Web UI for Storage Administration + +A new opt-in `linstor-gui` system package deploys **LINBIT's linstor-gui web UI** alongside the LINSTOR controller with mTLS client authentication, non-root security context, and ClusterIP-only service. An optional **Keycloak-protected Ingress** (via oauth2-proxy) can be enabled for SSO-authenticated browser access when OIDC is configured on the platform ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2382, #2390). + +### VM Default Images: Out-of-the-Box VM Provisioning + +The new `vm-default-images` package provides a curated set of **cluster-wide virtual machine images** (Ubuntu, Debian, CentOS Stream, and others) as pre-populated DataVolumes. The package is opt-in via the `iaas` bundle and defaults to replicated storage for high availability. A companion migration (migration 38) renames legacy `vm-image-*` DataVolumes to the new `vm-default-images-*` naming scheme. The `vm-disk` chart also gains a new "disk" source type for cloning from existing vm-disks in the same namespace ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2258). + +### WorkloadsReady Condition and Events Tab + +Applications now expose a **WorkloadsReady** condition on their status by querying associated WorkloadMonitor resources, giving operators a single place to check whether all underlying workloads (Deployments, StatefulSets, DaemonSets) are healthy. The dashboard gains a new **Events tab** showing namespace-scoped Kubernetes events for each application, with fallback to `.firstTimestamp` when `.eventTime` is absent. A bug where WorkloadMonitor's `Operational` status was never persisted is also fixed ([**@lexfrei**](https://github.com/lexfrei) in #2356). + +### Cross-Namespace VM Backup Restore + +The backup system now supports **restoring VMInstance backups into a different namespace** (cross-namespace copy restores), with IP/MAC preservation and safe rename semantics. In-place backup/restores for VMDisk and VMInstance are improved: HelmReleases and DataVolumes are properly handled, and Velero failure messages are propagated to the Application status. The backup status structure has been refactored to store underlying resources as a generic opaque JSON object, enabling arbitrary application-specific metadata ([**@androndo**](https://github.com/androndo) in #2251, #2329, #2319). + +## Major Features and Improvements + +* **[api] Reject tenant names with dashes at Create time**: Enforces alphanumeric-only naming for Tenants at the API level, preventing names with hyphens that would silently fail during Helm reconciliation. A corresponding regex tightening and regression test suite hardens the validation ([**@lexfrei**](https://github.com/lexfrei) in #2380). + +* **[platform] Validate computed tenant namespace length**: Rejects Tenant creation when the computed ancestor-chain namespace would exceed the 63-character Kubernetes namespace limit, preventing opaque HelmRelease reconcile errors downstream ([**@lexfrei**](https://github.com/lexfrei) in #2376). + +* **[vm-instance] Rename subnets to networks and add dropdown selector**: Renames the misleading `subnets` field to `networks` in VMInstance for clarity, adds a dropdown selector for available networks in the dashboard form, and includes a migration to copy existing `subnets` values. The old field remains supported for backward compatibility ([**@sircthulhu**](https://github.com/sircthulhu) in #2263). + +* **[keycloak] Enable injecting themes**: Cozystack administrators can now inject custom Keycloak themes via `initContainers` for UI white-labeling and customization ([**@lllamnyp**](https://github.com/lllamnyp) in #2142). + +* **[keycloak-configure] Add email verification and SMTP configuration**: Adds configurable Keycloak settings for user self-registration, email verification, and SMTP server configuration, enabling automated user onboarding flows ([**@BROngineer**](https://github.com/BROngineer) in #2318). + +* **[postgres] Hardcode PostgreSQL 17 for monitoring databases**: Pins PostgreSQL 17.7 images for system databases (Grafana, Alerta, Harbor, Keycloak, SeaweedFS) and adds migration 37 to backfill `spec.version=v17` for existing PostgreSQL resources, preventing CNPG from defaulting to PostgreSQL 18 *(backported to v1.2.1)* ([**@IvanHunters**](https://github.com/IvanHunters) in #2304). + +* **[hack] Add host runtime preflight check**: New `check-host-runtime.sh` script and `make preflight` target that warns operators when a standalone containerd or docker runtime is running alongside the embedded k3s runtime, helping diagnose container runtime conflicts ([**@lexfrei**](https://github.com/lexfrei) in #2371). + +* **[hack] Add check-readiness.sh diagnostic script**: A new diagnostic script for tracking platform reconciliation by checking readiness of Packages, ArtifactGenerators, ExternalArtifacts, and HelmReleases, with support for watch mode and continuous monitoring ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2294). + +* **[mariadb] Always enable replication for consistent service naming**: MariaDB now always enables replication, creating `-primary`/`-secondary` services even for single-replica instances. This fixes dashboard visibility and backup functionality for single-replica setups ([**@sircthulhu**](https://github.com/sircthulhu) in #2279). + +* **[platform] Prevent installed packages deletion**: Adds `helm.sh/resource-policy: keep` annotation to packages, preventing automatic deletion when packages are disabled and restoring documented behavior *(backported to v1.2.1)* ([**@kvaps**](https://github.com/kvaps) in #2273). + +## Bug Fixes + +* **[cilium] Opt-out of cri-containerd.apparmor.d for nsenter init containers**: Opts cilium-agent init containers out of the `cri-containerd.apparmor.d` AppArmor profile on non-Talos variants, fixing `Init:CrashLoopBackOff` on Ubuntu 22.04+ and Debian *(backported to v1.2.2)* ([**@lexfrei**](https://github.com/lexfrei) in #2370). + +* **[virtual-machine] Exclude external VM services from Cilium BPF LB**: Adds `service-proxy-name: cozy-proxy` label to VM LoadBalancer services, telling Cilium to skip BPF processing. Fixes inter-tenant connectivity via public LB IPs and WholeIP functionality on Cilium 1.19+ *(backported to v1.2.2)* ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #2357). + +* **[monitoring] Fix infra dashboards missing in default variant**: Includes `cozy-monitoring` namespace in the dashboard rendering condition, fixing infrastructure Grafana dashboards not rendering in the default platform variant *(backported to v1.2.2)* ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #2365). + +* **[postgres] Fix system PostgreSQL images to 17.7-standard-trixie**: Normalizes system PostgreSQL image tags to use `17.7-standard-trixie` variant with migration logic for existing CNPG clusters *(backported to v1.2.2)* ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2364). + +* **[build] Filter git describe to match only v\* tags**: Adds `--match 'v*'` to `git describe` calls, preventing API subtags from being picked up instead of release tags and producing invalid Docker image tags *(backported to v1.2.2)* ([**@kvaps**](https://github.com/kvaps) in #2386). + +* **[platform] Fix resource allocation ratios not propagated to packages**: Restores propagation of CPU, memory, and ephemeral-storage allocation ratios to managed applications and KubeVirt, which were silently ignored since the bundle restructure *(backported to v1.2.1)* ([**@sircthulhu**](https://github.com/sircthulhu) in #2296). + +* **[kubernetes] Set explicit ephemeral-storage on virt-launcher pods**: Sets explicit `domain.resources` with ephemeral-storage on VirtualMachine spec to prevent virt-launcher pods from being evicted due to LimitRange defaults being too low for actual emptyDisk capacity ([**@kvaps**](https://github.com/kvaps) in #2317). + +* **[multus] Pin master CNI to 05-cilium.conflist**: Prevents a boot-time race condition where multus could auto-detect kube-ovn's conflist instead of Cilium's *(backported to v1.2.1)* ([**@kvaps**](https://github.com/kvaps) in #2315). + +* **[multus] Build custom image with DEL cache fix**: Fixes sandbox cleanup deadlock when CNI ADD never completes, preventing stale sandbox name reservations from permanently blocking pod creation *(backported to v1.2.1)* ([**@kvaps**](https://github.com/kvaps) in #2313). + +* **[linstor] Set verify-alg to crc32c**: Prevents DRBD connection failures on kernels where `crct10dif` is unavailable (e.g., Talos v1.12.6 with kernel 6.18.18) *(backported to v1.2.1)* ([**@kvaps**](https://github.com/kvaps) in #2303). + +* **[linstor] Preserve TCP ports during toggle-disk operations**: Fixes TCP port mismatches after toggle-disk operations that could cause DRBD resources to enter StandAlone state *(backported to v1.2.1)* ([**@kvaps**](https://github.com/kvaps) in #2292). + +## Dependencies & Version Updates + +* **[linstor] Update piraeus-server to v1.33.2 with selected backports**: Bumps LINSTOR server from v1.33.1 to v1.33.2 with backported patches for stale bitmap adjust retry, LUKS2 header sizing, and optimal I/O size detection *(backported to v1.2.2)* ([**@kvaps**](https://github.com/kvaps) in #2331). + +* **[kamaji] Update to 26.3.5-edge, drop upstreamed patches**: Updates Kamaji from edge-26.2.4 to 26.3.5-edge and removes two patches accepted upstream. Adds configurable probe tuning and DataStore readiness conditions ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2260). + +* **[talm] Release v0.23.0, v0.23.1, v0.24.0** (github.com/cozystack/talm): Migrates to the Talos v1.12 multi-document machine config format ([**@lexfrei**](https://github.com/lexfrei) in cozystack/talm#116); fixes template rendering in `apply` command to resolve lookups ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/talm#119); bumps dependencies and modernizes codebase ([**@lexfrei**](https://github.com/lexfrei) in cozystack/talm#124). + +* **[ansible-cozystack] Release v1.2.1, v1.2.2** (github.com/cozystack/ansible-cozystack): Exposes `publishing.externalIPs` and tenant-root ingress via role variables ([**@lexfrei**](https://github.com/lexfrei) in cozystack/ansible-cozystack#30); adds comprehensive node prerequisites audit ([**@lexfrei**](https://github.com/lexfrei) in cozystack/ansible-cozystack#27); replaces `ansible.utils.ipaddr` with a stdlib-based test plugin ([**@lexfrei**](https://github.com/lexfrei) in cozystack/ansible-cozystack#24). + +## Security + +* **docs: add SECURITY.md**: Adds vulnerability reporting procedures, disclosure expectations, and supported release lines ([**@kvaps**](https://github.com/kvaps) in #2230). + +* **docs: add OpenSSF Best Practices badge to README**: Adds the OpenSSF Best Practices passing badge to the project README ([**@lexfrei**](https://github.com/lexfrei) in #2320). + +## Development, Testing, and CI/CD + +* **[ci] Replace cozystack-bot PAT with cozystack-ci GitHub App**: Replaces the long-lived cozystack-bot personal access token with short-lived, scoped tokens from the cozystack-ci GitHub App across all CI release workflows ([**@tym83**](https://github.com/tym83) in #2351; [**@kvaps**](https://github.com/kvaps) in #2383, #2392). + +* **[ci] Add Gemini Code Assist and CodeRabbit configuration**: Adds repository-level configuration for AI code reviewers with ignore patterns for vendored/generated code and incremental review settings ([**@lexfrei**](https://github.com/lexfrei) in #2385). + +* **[ci] Make tags workflow idempotent on re-runs**: Fixes CI to force-update API subtags and handle re-runs gracefully ([**@kvaps**](https://github.com/kvaps)). + +* **[tests] Fix Kafka E2E test timeout and retry race condition**: Increases Kafka E2E test timeout from 60s to 300s and fixes a retry race condition where `kubectl apply` could hit a still-deleting resource ([**@lexfrei**](https://github.com/lexfrei) in #2358). + +* **docs: adopt Conventional Commits for commit and PR titles**: Standardizes commit and PR title format to `type(scope): description` across all contributing docs and the PR template ([**@lexfrei**](https://github.com/lexfrei) in #2395). + +* **docs(ci): require screenshots for UI changes in PR template**: Adds a mandatory screenshots section to the PR template for UI-related changes ([**@kitsunoff**](https://github.com/kitsunoff) in #2407). + +## Documentation + +* **[website] Add ApplicationDefinition naming convention reference**: Documents how `cozystack-api` resolves kinds to their backing definitions ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#478). + +* **[website] Document Talos / talosctl / Cozystack version pairing**: Adds version compatibility matrix for installation ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#484). + +* **[website] Document namespace layout and parent/child derivation**: Explains tenant namespace hierarchy and parent/child namespace derivation rules ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#479). + +* **[website] Document the checkbox-then-edit-CR customization pattern for tenants**: Describes the workflow for customizing tenant settings via the CR after initial checkbox-based creation ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#485). + +* **[website] Add custom Keycloak themes documentation**: Covers the theme image contract, configuration, `imagePullSecrets`, and theme activation in the Keycloak admin console ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#463). + +* **[website] Add bonding (LACP) configuration how-to guide**: Covers network bonding configuration for Cozystack installations ([**@sircthulhu**](https://github.com/sircthulhu) in cozystack/website#459). + +* **[website] Improve registry mirrors for tenant Kubernetes in air-gapped guide**: Improved documentation for configuring registry mirrors in air-gapped environments ([**@sircthulhu**](https://github.com/sircthulhu) in cozystack/website#461). + +* **[website] Rewrite guide for ApplicationDefinition API (external-apps)**: Comprehensive rewrite of the external apps guide using the ApplicationDefinition API ([**@kitsunoff**](https://github.com/kitsunoff) in cozystack/website#488). + +* **[website] Add documentation for Go types usage**: Guide for using generated Go types for Cozystack managed applications as a Go module ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#465). + +* **[website] Update backup/restore documentation for VMI/VMDisk**: Updated backup documentation with VM instance and VM disk restore improvements ([**@androndo**](https://github.com/androndo) in cozystack/website#466). + +* **[website] Add OSS Health pages and OpenSSF badge**: Added OSS Health section with OpenSSF Scorecard and Best Practices badge to the website ([**@tym83**](https://github.com/tym83) in cozystack/website#470). + +* **[website] Add CozySummit Virtual 2026 program announcement**: Published the CozySummit Virtual 2026 program announcement blog post ([**@tym83**](https://github.com/tym83) in cozystack/website#472). + +* **[website] Add missing release announcements for v0.1–v0.41**: Backfilled missing release announcement blog posts for historical Cozystack versions ([**@tym83**](https://github.com/tym83) in cozystack/website#468). + +* **[website] Fix broken links and stale anchors across v1 docs**: Fixes 14 broken links and stale talm anchors ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#486). + +* **[website] Prefix bundle package names with cozystack. in v1 examples**: Corrects package naming in documentation examples ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#482). + +* **[website] Finish isolated-field removal and document opt-in policy labels**: Removes obsolete `isolated` field from tenant documentation and documents the new approach ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#481). + +* **[website] Add --take-ownership flag and describe networking.* fields**: Documents the `--take-ownership` flag and `networking.*` fields in the installation guide ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#480). + +* **[website] Fix KubeOVN MASTER_NODES example path and key in troubleshooting**: Corrects the MASTER_NODES example path ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#483). + +* **[external-apps-example] Replace MongoDB example with Minecraft apps**: Refactors the external apps example to use ApplicationDefinition API with Minecraft server applications ([**@lexfrei**](https://github.com/lexfrei) in cozystack/external-apps-example#2). + +## Governance + +* **Add Mattia Eleuteri ([@mattia-eleuteri](https://github.com/mattia-eleuteri)) as Maintainer**: CSI, Storage, Networking & Security ([**@tym83**](https://github.com/tym83) in #2345). + +* **Add Matthieu Robin ([@matthieu-robin](https://github.com/matthieu-robin)) as Maintainer**: Managed applications, platform quality, and benchmarking ([**@tym83**](https://github.com/tym83) in #2346). + +## Contributors + +We'd like to thank all contributors who made this release possible: + +* [**@androndo**](https://github.com/androndo) +* [**@BROngineer**](https://github.com/BROngineer) +* [**@IvanHunters**](https://github.com/IvanHunters) +* [**@kitsunoff**](https://github.com/kitsunoff) +* [**@kvaps**](https://github.com/kvaps) +* [**@lexfrei**](https://github.com/lexfrei) +* [**@lllamnyp**](https://github.com/lllamnyp) +* [**@mattia-eleuteri**](https://github.com/mattia-eleuteri) +* [**@myasnikovdaniil**](https://github.com/myasnikovdaniil) +* [**@sircthulhu**](https://github.com/sircthulhu) +* [**@tym83**](https://github.com/tym83) + +--- + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.2.0...v1.3.0-rc.1 diff --git a/docs/changelogs/v1.3.0.md b/docs/changelogs/v1.3.0.md new file mode 100644 index 00000000..60c800a8 --- /dev/null +++ b/docs/changelogs/v1.3.0.md @@ -0,0 +1,238 @@ + + +# Cozystack v1.3.0 + +Cozystack v1.3.0 brings **storage-aware pod scheduling** via a LINSTOR scheduler extender, a managed **LINSTOR GUI** web console with Keycloak SSO, a curated **VM Default Images** catalog for out-of-the-box virtual-machine provisioning, a new **WorkloadsReady / Events** observability surface with S3 bucket metering, and **cross-namespace VMInstance backup restore** with a full **RestoreJob dashboard** flow. The release also ships stricter tenant-name validation, VMInstance network-selector improvements, Keycloak theme injection and SMTP configuration, a host-runtime preflight check, and rolls up every fix from the v1.2.1 → v1.2.4 patch line. + +> **Note:** Items marked *(backported to v1.2.x)* were also shipped in v1.2.1, v1.2.2, v1.2.3, or v1.2.4 patch releases. + +## Feature Highlights + +### Storage-Aware Scheduling via the LINSTOR Extender + +The `cozystack-scheduler` now calls a **LINSTOR scheduler extender** for storage-locality-aware pod placement. When a pod declares both a `SchedulingClass` and LINSTOR-backed PVCs, the scheduler consults LINSTOR to prefer nodes where volume replicas already exist — reducing cross-node replication traffic and improving I/O latency for storage-heavy workloads such as databases, object stores, and VMs. + +The integration builds on the existing `SchedulingClass` tenant workload placement system introduced in v1.2.0 and requires no tenant-side configuration — workloads simply benefit once a SchedulingClass is assigned. Administrators can mix storage locality with the existing data-center / hardware-generation constraints defined on SchedulingClass CRs ([**@lllamnyp**](https://github.com/lllamnyp) in #2330). + +### LINSTOR GUI: Managed Web Console for Storage Administration + +A new opt-in `linstor-gui` system package deploys **LINBIT's linstor-gui web UI** alongside the LINSTOR controller with mTLS client authentication, non-root security context, and a ClusterIP-only service by default. When OIDC is configured on the platform, an optional **Keycloak-protected Ingress** (via oauth2-proxy) exposes the UI for browser access. Access is restricted to members of the `cozystack-cluster-admin` Keycloak group, consistent with host-cluster admin RBAC, and the gatekeeper blocks in-app LINSTOR authentication setup at the nginx proxy layer so the managed configuration cannot be subverted through the UI. + +Operators who prefer CLI access keep the existing `linstor` command; the GUI is strictly additive and stays disabled by default ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2382, #2390, #2415, #2419). + +### VM Default Images: Out-of-the-Box VM Provisioning + +The new `vm-default-images` package provides a curated set of **cluster-wide virtual-machine images** (Ubuntu, Debian, CentOS Stream, and others) as pre-populated DataVolumes, so tenants can provision VMs against well-known base images without first having to upload them. The package is opt-in via the `iaas` bundle and defaults to replicated storage for high availability. Migration 38 renames legacy `vm-image-*` DataVolumes to the new `vm-default-images-*` naming scheme, and the `vm-disk` chart gains a new "disk" source type for cloning from existing vm-disks in the same namespace ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2258). + +### Application Observability: WorkloadsReady, Events, and S3 Bucket Metering + +Applications now expose a **WorkloadsReady** condition on their status by querying associated WorkloadMonitor resources, giving operators a single place to check whether all underlying workloads (Deployments, StatefulSets, DaemonSets, PVCs) are healthy. The dashboard gains a new **Events tab** showing namespace-scoped Kubernetes events per application, with fallback to `.firstTimestamp` when `.eventTime` is absent. A long-standing bug where WorkloadMonitor's `Operational` status was never persisted is fixed in the same change ([**@lexfrei**](https://github.com/lexfrei) in #2356). + +The WorkloadMonitor reconciler is extended to track **COSI BucketClaim** objects as first-class Workloads, and the bucket controller now queries SeaweedFS logical and physical bucket-size metrics from VictoriaMetrics via a namespace-scoped monitoring endpoint, enabling S3 billing integration on par with Pods and PVCs ([**@kitsunoff**](https://github.com/kitsunoff) in #2391). Workloads are also enriched with `workloads.cozystack.io/resource-preset` and source-object labels so downstream billing pipelines can correlate monitors with the tenant preset that produced them ([**@androndo**](https://github.com/androndo) in #2416). + +### Cross-Namespace VM Backup Restore and RestoreJob Dashboard + +The backup system now supports **restoring VMInstance backups into a different namespace** (cross-namespace copy restores) with IP/MAC preservation and safe rename semantics. In-place backup and restore flows for VMDisk and VMInstance are improved: HelmReleases and DataVolumes are properly handled, and Velero failure messages are propagated to the Application status. The backup status structure has been refactored to store underlying resources as a generic opaque JSON object, enabling arbitrary application-specific metadata without status-schema churn ([**@androndo**](https://github.com/androndo) in #2251, #2319, #2329). + +The dashboard now ships a complete **RestoreJob experience**: list view, details page, create form, and sidebar entry, with a "Same as backup" fallback rendering when `spec.targetApplicationRef` is omitted. Non-CRD-backed sidebar factories (`kube-*`, `plan`, `backupjob`, `backup`, `restorejob`) are marked static so they pick up consistent managed-by labels across reconciles ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2437). + +## Major Features and Improvements + +* **[api] Reject tenant names with dashes at Create time**: Enforces alphanumeric-only naming for Tenants at the API level, preventing names with hyphens that would silently fail during Helm reconciliation. A corresponding regex tightening and regression test suite hardens the validation ([**@lexfrei**](https://github.com/lexfrei) in #2380). + +* **[platform] Validate computed tenant namespace length**: Rejects Tenant creation when the computed ancestor-chain namespace would exceed the 63-character Kubernetes namespace limit, preventing opaque HelmRelease reconcile errors downstream ([**@lexfrei**](https://github.com/lexfrei) in #2376). + +* **[vm-instance] Rename subnets to networks and add dropdown selector**: Renames the misleading `subnets` field to `networks` in VMInstance for clarity, adds a dropdown selector for available networks in the dashboard form, and includes migration 36 to copy existing `subnets` values. The old field remains supported for backward compatibility ([**@sircthulhu**](https://github.com/sircthulhu) in #2263). + +* **[keycloak] Enable injecting themes**: Cozystack administrators can now inject custom Keycloak themes via `initContainers` for UI white-labeling and customization ([**@lllamnyp**](https://github.com/lllamnyp) in #2142). + +* **[keycloak-configure] Add email verification and SMTP configuration**: Adds configurable Keycloak settings for user self-registration, email verification, and SMTP server configuration, enabling automated user onboarding flows ([**@BROngineer**](https://github.com/BROngineer) in #2318). + +* **[postgres] Pin system PostgreSQL to 17.7-standard-trixie**: Pins the PostgreSQL image for system databases (Grafana, Alerta, Harbor, Keycloak, SeaweedFS) to `17.7-standard-trixie` across chart templates and `values.yaml`, and ships migration 37 to patch existing CNPG Cluster `imageName` fields to the same variant (handling unset, any PG 17 tag, and bare-version tags). This prevents CNPG from defaulting to PostgreSQL 18 and locks system databases to the trixie variant consistent with the monitoring stack requirements *(related backports shipped in v1.2.1 via #2309 and v1.2.2 via #2364)* ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2369). + +* **[platform] Prevent installed packages deletion**: Adds the `helm.sh/resource-policy: keep` annotation to platform packages so disabling a package no longer triggers automatic Helm deletion, restoring the documented behavior where operators must explicitly delete a package *(backported to v1.2.1)* ([**@kvaps**](https://github.com/kvaps) in #2273). + +* **[mariadb] Always enable replication for consistent service naming**: MariaDB now always enables replication, creating `-primary`/`-secondary` services even for single-replica instances. This fixes dashboard visibility and backup functionality for single-replica setups ([**@sircthulhu**](https://github.com/sircthulhu) in #2279). + +* **[hack] Add host runtime preflight check**: New `check-host-runtime.sh` script and `make preflight` target that warns operators when a standalone containerd or docker runtime is running alongside the embedded k3s runtime, helping diagnose container-runtime conflicts early in an installation ([**@lexfrei**](https://github.com/lexfrei) in #2371). + +* **[hack] Add check-readiness.sh diagnostic script**: A new diagnostic script for tracking platform reconciliation by checking readiness of Packages, ArtifactGenerators, ExternalArtifacts, and HelmReleases, with support for watch mode and continuous monitoring ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2294). + +* **[platform] Add resourcePreset labels to WorkloadMonitor labels**: WorkloadMonitor labels with the `workloads.cozystack.io/` prefix are now propagated onto created Workloads; created Workloads always include the reserved `workloads.cozystack.io/monitor` label, and Helm app charts add `workloads.cozystack.io/resource-preset` metadata to WorkloadMonitor manifests, enabling downstream billing pipelines to correlate monitors with the tenant preset that produced them ([**@androndo**](https://github.com/androndo) in #2416). + +## Bug Fixes + +* **[platform] Migrate ACME HTTP-01 to ingressClassName API**: Switches ACME HTTP-01 issuance from the deprecated `acme.cert-manager.io/http01-ingress-class` annotation to the modern `ingressClassName` field on `ClusterIssuer` and solver pods. Previously, ClusterIssuers referenced a non-existent `nginx` class while each Ingress individually overrode it via annotation — producing `ingressClassName and class cannot be set at the same time` errors when tenants attempted to migrate to the modern field. The migration is atomic: both the ClusterIssuer and consuming Ingresses are updated together *(backported to v1.2.4)* ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2436). + +* **[harbor] Remove incorrect tenant module flags**: Harbor is a PaaS service, not a tenant module. Incorrect `spec.dashboard.module: true` and `internal.cozystack.io/tenantmodule` flags caused Harbor to appear in the sidebar "Modules" section and be misclassified by controllers handling tenant modules. The flags are now removed so Harbor is displayed in its proper PaaS category and is no longer treated as a tenant-scoped HelmRelease ([**@kvaps**](https://github.com/kvaps) in #2444). + +* **[kube-ovn] Resolve kubeovn-plunger RBAC forbidden on deployments**: Grants `kube-ovn-plunger` the RBAC needed to list Deployments so it can reconcile `ovn-central`, fixing `deployments.apps is forbidden` errors in `cozy-kubeovn` ([**@kvaps**](https://github.com/kvaps) in #2441). + +* **[cilium] Opt-out of cri-containerd.apparmor.d for nsenter init containers**: Opts cilium-agent init containers out of the `cri-containerd.apparmor.d` AppArmor profile on non-Talos variants (`cilium-generic`, `kubeovn-cilium-generic`), fixing `Init:CrashLoopBackOff` on Ubuntu 22.04+ and Debian where the profile denies `nsenter` namespace entry. Talos variants are untouched as Talos does not load the AppArmor LSM *(backported to v1.2.2)* ([**@lexfrei**](https://github.com/lexfrei) in #2370). + +* **[virtual-machine] Exclude external VM services from Cilium BPF LB**: Adds the `service.kubernetes.io/service-proxy-name: cozy-proxy` label to VM LoadBalancer services with `external: true`, telling Cilium to skip BPF processing entirely. Fixes inter-tenant connectivity via public LB IPs (Cilium's DNAT caused cross-tenant pod-to-pod flow classification, triggering CiliumClusterwideNetworkPolicy blocks) and restores WholeIP behavior on Cilium 1.19+ where wildcard service drop entries previously blocked traffic to LB IPs on undeclared ports *(backported to v1.2.2)* ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #2357). + +* **[monitoring] Fix infra dashboards missing in default variant**: Includes the `cozy-monitoring` namespace in the dashboard rendering condition, fixing infrastructure Grafana dashboards not rendering in the default platform variant (only the `tenant-root` namespace was previously checked) *(backported to v1.2.2)* ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #2365). + +* **[build] Filter git describe to match only v* tags**: Adds `--match 'v*'` to all `git describe` calls in `hack/common-envs.mk`, preventing the `api/apps/v1alpha1/vX.Y.Z` subtag from being picked up instead of the release tag and producing invalid Docker image tags *(backported to v1.2.2)* ([**@kvaps**](https://github.com/kvaps) in #2386). + +* **[platform] Fix resource allocation ratios not propagated to packages**: Restores propagation of `cpuAllocationRatio`, `memoryAllocationRatio`, and `ephemeralStorageAllocationRatio` from `platform/values.yaml` to the `cozystack-values` Secret that managed applications and KubeVirt read, fixing a regression introduced in the bundle restructure that silently ignored operator-configured ratios *(backported to v1.2.1)* ([**@sircthulhu**](https://github.com/sircthulhu) in #2296). + +* **[kubernetes] Set explicit ephemeral-storage on virt-launcher pods**: Sets explicit `domain.resources` ephemeral-storage on the VirtualMachine spec to prevent virt-launcher pods from being evicted because LimitRange defaults were too small for the actual emptyDisk capacity *(backported to v1.2.3)* ([**@kvaps**](https://github.com/kvaps) in #2317). + +* **[multus] Pin master CNI to 05-cilium.conflist**: Prevents a boot-time race where multus could auto-detect kube-ovn's conflist instead of Cilium's, which would cause pods to bypass the Cilium chain entirely and lose their endpoint *(backported to v1.2.1)* ([**@kvaps**](https://github.com/kvaps) in #2315). + +* **[multus] Build custom image with DEL cache fix**: Fixes sandbox cleanup deadlock when CNI ADD never completes, preventing stale sandbox name reservations from permanently blocking pod creation after a node disruption *(backported to v1.2.1)* ([**@kvaps**](https://github.com/kvaps) in #2313). + +* **[linstor] Set verify-alg to crc32c**: Prevents DRBD connection failures on kernels where `crct10dif` is unavailable (e.g., Talos v1.12.6 with kernel 6.18.18) by setting the LINSTOR verify-alg controller default to `crc32c` *(backported to v1.2.1)* ([**@kvaps**](https://github.com/kvaps) in #2303). + +* **[linstor] Preserve TCP ports during toggle-disk operations**: Saves existing TCP ports into the `LayerPayload` before `removeLayerData()` deletes them, preventing DRBD resources from entering StandAlone state when a satellite misses the resulting update *(backported to v1.2.1)* ([**@kvaps**](https://github.com/kvaps) in #2292). + +* **[linstor] Increase satellite startup probe failure threshold**: Raises the LINSTOR satellite `startupProbe` `failureThreshold` from 3 to 30 (30s → 300s) in the `LinstorSatelliteConfiguration` pod template, giving satellites with slow storage initialization enough time to come up without being killed and restarted ([**@Arsolitt**](https://github.com/Arsolitt) in #2425). + +## Security + +* **docs: add SECURITY.md**: Adds vulnerability reporting procedures, disclosure expectations, and supported release lines ([**@kvaps**](https://github.com/kvaps) in #2230). + +* **docs: add OpenSSF Best Practices badge to README**: Adds the OpenSSF Best Practices passing badge to the project README ([**@lexfrei**](https://github.com/lexfrei) in #2320). + +## Dependencies & Version Updates + +* **[kube-ovn] Bump kube-ovn to v1.15.10 with port-group regression fix**: Updates `packages/system/kubeovn` to upstream v1.15.10 (from v1.15.3) and carries a patch for `pkg/controller/pod.go` that preserves a VM LSP's port-group memberships when Kubernetes GCs a completed virt-launcher pod while another virt-launcher pod of the same VM is still running. Without the patch, the destination pod of a successful live migration lost its security groups, network policies, and node-scoped routing until `kube-ovn-controller` was restarted ([**@kvaps**](https://github.com/kvaps) in #2443). + +* **[monitoring] Upgrade victoria-metrics-operator to v0.68.4**: Bumps the vendored `victoria-metrics-operator` Helm chart from 0.59.1 to 0.61.0 (operator appVersion v0.68.1 → v0.68.4), picking up upstream fixes for `VMPodScrape` port routing on VMAgent/VLAgent and `StatefulSet` pod deletion (not eviction) when `maxUnavailable=100%` ([**@lexfrei**](https://github.com/lexfrei) in #2426). + +* **[linstor] Update piraeus-server to v1.33.2 with selected backports**: Bumps LINSTOR server from v1.33.1 to v1.33.2 with backported patches for stale bitmap adjust retry, LUKS2 header sizing, optimal I/O size detection, and the maintainer implementation. All patches verified against upstream v1.33.2 with `git apply --check` and `gradlew compileJava` *(backported to v1.2.2)* ([**@kvaps**](https://github.com/kvaps) in #2331). + +* **[kamaji] Update to 26.3.5-edge, drop upstreamed patches**: Updates Kamaji from edge-26.2.4 to 26.3.5-edge and removes two patches accepted upstream. Adds configurable probe tuning and DataStore readiness conditions ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2260). + +* **[talm] Release v0.23.0, v0.23.1, v0.24.0** (github.com/cozystack/talm): Migrates to the Talos v1.12 multi-document machine config format ([**@lexfrei**](https://github.com/lexfrei) in cozystack/talm#116); renders templates online in `apply` to resolve lookups ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/talm#119); bumps dependencies and modernizes the codebase ([**@lexfrei**](https://github.com/lexfrei) in cozystack/talm#124). + +* **[ansible-cozystack] Release v1.2.1, v1.2.2, v1.2.4** (github.com/cozystack/ansible-cozystack): Exposes `publishing.externalIPs` and tenant-root ingress via role variables ([**@lexfrei**](https://github.com/lexfrei) in cozystack/ansible-cozystack#30); adds a comprehensive node prerequisites audit ([**@lexfrei**](https://github.com/lexfrei) in cozystack/ansible-cozystack#27); replaces `ansible.utils.ipaddr` with a stdlib-based test plugin ([**@lexfrei**](https://github.com/lexfrei) in cozystack/ansible-cozystack#24); adds `v` prefix to collection version in requirements.yml examples ([**@lexfrei**](https://github.com/lexfrei) in cozystack/ansible-cozystack#23); tracks installer releases v1.2.1 through v1.2.4 ([**@app/renovate**](https://github.com/apps/renovate) in cozystack/ansible-cozystack#20, #22, #29, #31, #32). + +## Development, Testing, and CI/CD + +* **[ci] Replace cozystack-bot PAT with cozystack-ci GitHub App**: Replaces the long-lived `cozystack-bot` personal access token with short-lived, scoped tokens from the `cozystack-ci` GitHub App across all release workflows (`tags.yaml`, `auto-release.yaml`, `pull-requests-release.yaml`), improving security and auditability of CI operations ([**@tym83**](https://github.com/tym83) in #2351; [**@kvaps**](https://github.com/kvaps) in #2383, #2392). + +* **[ci] Add Gemini Code Assist and CodeRabbit configuration**: Adds repository-level configuration for AI code reviewers with ignore patterns for vendored/generated code and incremental review settings ([**@lexfrei**](https://github.com/lexfrei) in #2385). + +* **[ci] Promote next/ trunk on new minor/major releases**: Updates `update-website-docs` in `tags.yaml` to match the new docs-versioning contract — the website repo replaces the old "pre-create `vX.Y/` draft directory" scheme with a permanent `content/en/docs/next/` trunk, and released version directories are promoted explicitly by the release workflow ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2433). + +* **[tests] Fix Kafka E2E test timeout and retry race condition**: Increases Kafka E2E test timeout from 60s to 300s and fixes a retry race where `kubectl apply` could hit a still-deleting resource ([**@lexfrei**](https://github.com/lexfrei) in #2358). + +* **docs: adopt Conventional Commits for commit and PR titles**: Standardizes commit and PR title format to `type(scope): description` across all contributing docs and the PR template ([**@lexfrei**](https://github.com/lexfrei) in #2395). + +* **docs(ci): require screenshots for UI changes in PR template**: Adds a mandatory screenshots section to the PR template for UI-related changes ([**@kitsunoff**](https://github.com/kitsunoff) in #2407). + +* **chore(maintenance): add @myasnikovdaniil to CODEOWNERS**: Adds @myasnikovdaniil to the default owners in `.github/CODEOWNERS` for automatic review requests ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2434). + +## Documentation + +* **[website] Add ApplicationDefinition naming convention reference**: Documents how `cozystack-api` resolves kinds to their backing definitions ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#478). + +* **[website] Document Talos / talosctl / Cozystack version pairing**: Adds a version compatibility matrix for installation ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#484). + +* **[website] Document namespace layout and parent/child derivation**: Explains tenant namespace hierarchy and parent/child namespace derivation rules ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#479). + +* **[website] Document the checkbox-then-edit-CR customization pattern for tenants**: Describes the workflow for customizing tenant settings via the CR after initial checkbox-based creation ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#485). + +* **[website] Add custom Keycloak themes documentation**: Covers the theme image contract, configuration, `imagePullSecrets`, and theme activation in the Keycloak admin console ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#463). + +* **[website] Add bonding (LACP) configuration how-to guide**: Covers network bonding configuration for Cozystack installations ([**@sircthulhu**](https://github.com/sircthulhu) in cozystack/website#459). + +* **[website] Improve registry mirrors for tenant Kubernetes in air-gapped guide**: Improves documentation for configuring registry mirrors in air-gapped environments ([**@sircthulhu**](https://github.com/sircthulhu) in cozystack/website#461). + +* **[website] Rewrite guide for ApplicationDefinition API (external-apps)**: Comprehensive rewrite of the external apps guide using the ApplicationDefinition API with Minecraft server examples ([**@kitsunoff**](https://github.com/kitsunoff) in cozystack/website#488). + +* **[website] Add documentation for Go types usage**: Guide for using generated Go types for Cozystack managed applications as a Go module ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#465). + +* **[website] Update backup/restore documentation for VMI/VMDisk**: Updates backup documentation with VM instance and VM disk restore improvements ([**@androndo**](https://github.com/androndo) in cozystack/website#466). + +* **[website] Refactor docs versions to major.minor variants**: Moves docs to major.minor versioning for the v1.x series ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#477). + +* **[website] Trunk-based versioning with permanent next/ directory**: Replaces the old "pre-create `vX.Y/` draft directory" scheme with a permanent `content/en/docs/next/` trunk; released version directories are promoted explicitly by `hack/release_next.sh` on new minor/major releases, and routing between `next/` and `vX.Y/` is Makefile-driven ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#495). + +* **[website] Add updated OpenAPI spec**: Updates the OpenAPI specification for managed applications reference ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#469). + +* **[website] Add OpenAPI spec download to GitHub Pages build**: Fixes the GitHub Pages build to include the OpenAPI spec download ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#494). + +* **[website] Add OSS Health pages and OpenSSF badge**: Adds OSS Health section with OpenSSF Scorecard and Best Practices badges to the website ([**@tym83**](https://github.com/tym83) in cozystack/website#470). + +* **[website] Add Telemetry page under OSS Health section**: Adds the Telemetry page with initial data seeding to the OSS Health docs ([**@tym83**](https://github.com/tym83) in cozystack/website#471, cozystack/website#504). + +* **[website] Blog: OSS Health section launch announcement**: Publishes the announcement blog post for the OSS Health section ([**@tym83**](https://github.com/tym83) in cozystack/website#474). + +* **[website] Fix OpenSSF canonical status URL**: Changes the OpenSSF canonical status URL from pt-BR to en ([**@tym83**](https://github.com/tym83) in cozystack/website#475). + +* **[website] Add CozySummit Virtual 2026 program announcement**: Publishes the CozySummit Virtual 2026 program announcement blog post ([**@tym83**](https://github.com/tym83) in cozystack/website#472). + +* **[website] Add missing release announcements for v0.1–v0.41**: Backfills missing release announcement blog posts for historical Cozystack versions ([**@tym83**](https://github.com/tym83) in cozystack/website#468). + +* **[website] Blog: managed PostgreSQL with synchronous replication**: Adds a post covering the managed PostgreSQL synchronous-replication feature ([**@tym83**](https://github.com/tym83) in cozystack/website#497). + +* **[website] Blog taxonomies and client-side filter UI**: Registers article-type and topic taxonomies and adds a client-side filter on the blog list page ([**@tym83**](https://github.com/tym83) in cozystack/website#499). + +* **[website] Add images frontmatter for social preview on existing posts**: Adds images frontmatter for social preview on existing blog posts ([**@tym83**](https://github.com/tym83) in cozystack/website#498). + +* **[website] Fix broken links and stale anchors across v1 docs**: Fixes 14 broken links and stale talm anchors ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#486). + +* **[website] Prefix bundle package names with cozystack. in v1 examples**: Corrects package naming in documentation examples ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#482). + +* **[website] Finish isolated-field removal and document opt-in policy labels**: Removes the obsolete `isolated` field from tenant documentation and documents the new opt-in policy labels approach ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#481). + +* **[website] Add --take-ownership flag and describe networking.* fields**: Documents the `--take-ownership` flag and `networking.*` fields in the installation guide ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#480). + +* **[website] Fix KubeOVN MASTER_NODES example path and key in troubleshooting**: Corrects the MASTER_NODES example path and key ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#483). + +* **[website] Add CLAUDE.md for AI agent guidance**: Adds a CLAUDE.md file describing the trunk-based docs architecture for AI agent guidance ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#489). + +* **[website] Update /docs/v1/ redirect to latest v1.2**: Updates the `/docs/v1/` redirect target to point to the latest v1.2 docs on GitHub Pages ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#492). + +* **[website] Remove nbykov from CODEOWNERS and CLAUDE.md**: Cleans up CODEOWNERS and CLAUDE.md entries ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in cozystack/website#491). + +* **[website] Add Ahrefs Analytics tracker**: Adds the Ahrefs Analytics tracker to the website ([**@tym83**](https://github.com/tym83) in cozystack/website#503). + +* **[website] Add breathing room between navbar and hero on OSS Health**: Minor styling fix for the OSS Health section ([**@tym83**](https://github.com/tym83) in cozystack/website#500). + +* **[website] Fix og social badge image and title**: Updates the social badge image and title ([**@tym83**](https://github.com/tym83) in cozystack/website#487). + +* **[website] Update managed apps reference for v1.2.1**: Automated managed-apps reference update ([**@cozystack-bot**](https://github.com/cozystack-bot) in cozystack/website#464). + +* **[external-apps-example] Replace MongoDB example with Minecraft apps**: Refactors the external apps example to use the ApplicationDefinition API with Minecraft server applications ([**@lexfrei**](https://github.com/lexfrei) in cozystack/external-apps-example#2). + +* **docs: update README introductory description**: Refines the platform positioning and improves clarity on core capabilities in the main README ([**@tym83**](https://github.com/tym83) in #2409). + +## Governance + +* **Add Mattia Eleuteri ([@mattia-eleuteri](https://github.com/mattia-eleuteri)) as Maintainer**: CSI, Storage, Networking & Security ([**@tym83**](https://github.com/tym83) in #2345). + +* **Add Matthieu Robin ([@matthieu-robin](https://github.com/matthieu-robin)) as Maintainer**: Managed applications, platform quality, and benchmarking ([**@tym83**](https://github.com/tym83) in #2346). + +## Contributors + +We'd like to thank all contributors who made this release possible: + +* [**@androndo**](https://github.com/androndo) +* [**@Arsolitt**](https://github.com/Arsolitt) +* [**@BROngineer**](https://github.com/BROngineer) +* [**@IvanHunters**](https://github.com/IvanHunters) +* [**@kitsunoff**](https://github.com/kitsunoff) +* [**@kvaps**](https://github.com/kvaps) +* [**@lexfrei**](https://github.com/lexfrei) +* [**@lllamnyp**](https://github.com/lllamnyp) +* [**@mattia-eleuteri**](https://github.com/mattia-eleuteri) +* [**@myasnikovdaniil**](https://github.com/myasnikovdaniil) +* [**@sircthulhu**](https://github.com/sircthulhu) +* [**@tym83**](https://github.com/tym83) + +### New Contributors + +We're excited to welcome our first-time contributors: + +* [**@Arsolitt**](https://github.com/Arsolitt) — First contribution! + +--- + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.2.0...v1.3.0 diff --git a/docs/changelogs/v1.3.1.md b/docs/changelogs/v1.3.1.md new file mode 100644 index 00000000..0f149d0d --- /dev/null +++ b/docs/changelogs/v1.3.1.md @@ -0,0 +1,37 @@ + + +# v1.3.1 (2026-04-28) + +Patch release covering a TenantNamespace IDOR fix in the API, a destructive `post-upgrade` hook removed from the etcd chart, kamaji controller stability, a `linstor-csi` bump that fixes live migration on Protocol-A/B DRBD resources, the missing `linstor-gui` build wiring, and a velero RBAC fix that unblocked installs on bundles without Velero. + +## Security + +* **fix(api): prevent IDOR in TenantNamespace Get and Watch handlers**: Two IDOR (Insecure Direct Object Reference) vulnerabilities allowed authenticated users to read TenantNamespace metadata they had no RoleBinding for. The `Get` and `Watch` handlers now go through a new `hasAccessToNamespace()` helper that lists RoleBindings scoped only to the target namespace (orders of magnitude faster than the previous all-cluster scan), returns `NotFound` instead of leaking existence on unauthorized access, and applies the same check on the `Watch` filter path. Includes regression tests for the unauthorized paths. ([**@IvanHunters**](https://github.com/IvanHunters) in #2471, backport #2524) + +## Features + +* **feat(linstor): bump linstor-csi to v1.10.6 with Protocol-C dual-attach fix**: Live migration of KubeVirt VMs on Protocol-A/B (async) DRBD volumes no longer fails with `Protocol C required`. `linstor-csi` v1.10.6 now installs a `Protocol=C` override on the resource-definition during dual-attach and reverts it on detach, so `replicated-async` StorageClasses and other Protocol-A/B resource groups support live migration without manual `drbdadm adjust` intervention. ([**@kvaps**](https://github.com/kvaps) in #2496, backport #2505) + +## Fixes + +* **fix(backups): move velero-configmap Role to velero chart**: The `backupstrategy-controller` (a default package) declared a Role/RoleBinding scoped to the `cozy-velero` namespace for managing `ResourceModifier` ConfigMaps. On bundles where Velero was not enabled, that namespace did not exist and the HelmRelease failed with `namespaces "cozy-velero" not found`, blocking installation. The Role/RoleBinding now lives in the velero chart, so it is created only when velero is actually deployed. ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2459, backport #2467) + +* **fix(etcd): remove destructive post-upgrade cert-regeneration hook**: The etcd chart ran a `post-upgrade` Helm hook on every upgrade that deleted etcd TLS Secrets (`etcd-ca-tls`, `etcd-peer-ca-tls`, `etcd-client-tls`, `etcd-peer-tls`, `etcd-server-tls`) and then deleted etcd pods, forcing cert-manager to re-issue the entire etcd CA chain. On clusters with Kamaji-managed tenant control planes this put every tenant `kube-apiserver` into CrashLoopBackOff until each DataStore was manually re-reconciled. The hook was a one-shot `2.6.0 → 2.6.1` migration that became a permanent footgun once chart versioning moved to `0.0.0+` (always `< 2.6.1` per semver) and after the underlying `rotationPolicy: Always` issue was fixed in `47d81f70`. The hook is now removed entirely. ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2462, backport #2511) + +* **fix(kamaji): increase memory limits and add startup probe**: The kamaji controller frequently entered CrashLoopBackOff due to OOMKills (exit 137) within ~20–25 seconds of startup, with the readiness probe failing while the controller was still finishing initialization. Memory limit raised from 500Mi to 512Mi, request from 100Mi to 256Mi, and a 60-second startup probe (12 attempts × 5s periods) is added so the controller has room to boot before liveness/readiness probes engage. ([**@IvanHunters**](https://github.com/IvanHunters) in #2421, backport #2491) + +## Build + +* **build(linstor): include linstor-gui in root image build target**: The `linstor-gui` package (added in #2382) was never wired into the root `Makefile`'s `build:` target, so CI never built or published the image. `ghcr.io/cozystack/cozystack/linstor-gui` returned `NAME_UNKNOWN` and `values.yaml` stayed pinned to `tag: 2.3.0` without a digest. The missing build line is added so the next CI run publishes the image and the per-package `Makefile` digest-pins `values.yaml` automatically. ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2498, backport #2518) + +## Contributors + +Thanks to everyone who contributed to this patch release: + +* [**@IvanHunters**](https://github.com/IvanHunters) +* [**@kvaps**](https://github.com/kvaps) +* [**@myasnikovdaniil**](https://github.com/myasnikovdaniil) + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.3.0...v1.3.1 diff --git a/docs/proposals/affinity-class.md b/docs/proposals/affinity-class.md deleted file mode 100644 index 090f627b..00000000 --- a/docs/proposals/affinity-class.md +++ /dev/null @@ -1,356 +0,0 @@ -# AffinityClass: Named Placement Classes for CozyStack Applications (Draft) - -## Concept - -Similar to StorageClass in Kubernetes, a new resource **AffinityClass** is introduced — a named abstraction over scheduling constraints. When creating an Application, the user selects an AffinityClass by name without knowing the details of the cluster topology. - -``` -StorageClass → "which disk" → PV provisioning -AffinityClass → "where to place" → Pod scheduling -``` - -## Design - -### 1. AffinityClass CRD - -A cluster-scoped resource created by the platform administrator: - -```yaml -apiVersion: cozystack.io/v1alpha1 -kind: AffinityClass -metadata: - name: dc1 -spec: - # nodeSelector that MUST be present on every pod of the application. - # Used for validation by the lineage webhook. - nodeSelector: - topology.kubernetes.io/zone: dc1 -``` - -```yaml -apiVersion: cozystack.io/v1alpha1 -kind: AffinityClass -metadata: - name: dc2 -spec: - nodeSelector: - topology.kubernetes.io/zone: dc2 -``` - -```yaml -apiVersion: cozystack.io/v1alpha1 -kind: AffinityClass -metadata: - name: gpu -spec: - nodeSelector: - node.kubernetes.io/gpu: "true" -``` - -An AffinityClass contains a `nodeSelector` — a set of key=value pairs that must be present in `pod.spec.nodeSelector` on every pod of the application. This is a contract: the chart is responsible for setting these selectors, the webhook is responsible for verifying them. - -### 2. Tenant: Restricting Available Classes - -Tenant gets `allowedAffinityClasses` and `defaultAffinityClass` fields: - -```yaml -apiVersion: apps.cozystack.io/v1alpha1 -kind: Tenant -metadata: - name: acme - namespace: tenant-root -spec: - defaultAffinityClass: dc1 # default class for applications - allowedAffinityClasses: # which classes are allowed - - dc1 - - dc2 - etcd: false - ingress: true - monitoring: false -``` - -These values are propagated to the `cozystack-values` Secret in the child namespace: - -```yaml -# Secret cozystack-values in namespace tenant-acme -stringData: - values.yaml: | - _cluster: - # ... existing cluster config - _namespace: - # ... existing namespace config - defaultAffinityClass: dc1 - allowedAffinityClasses: - - dc1 - - dc2 -``` - -### 3. Application: Selecting a Class - -Each application can specify an `affinityClass`. If not specified, the `defaultAffinityClass` from the tenant is used: - -```yaml -apiVersion: apps.cozystack.io/v1alpha1 -kind: Postgres -metadata: - name: main-db - namespace: tenant-acme -spec: - affinityClass: dc1 # explicit selection - replicas: 3 -``` - -```yaml -apiVersion: apps.cozystack.io/v1alpha1 -kind: Redis -metadata: - name: cache - namespace: tenant-acme -spec: - # affinityClass not specified → uses tenant's defaultAffinityClass (dc1) - replicas: 2 -``` - -### 4. How affinityClass Reaches the HelmRelease - -When creating an Application, the API server (`pkg/registry/apps/application/rest.go`): - -1. Extracts `affinityClass` from `spec` (or uses the default from `cozystack-values`) -2. Records `affinityClass` as a **label on the HelmRelease**: - ``` - apps.cozystack.io/affinity-class: dc1 - ``` -3. Resolves AffinityClass to `nodeSelector` and passes it into HelmRelease values as `_scheduling`: - ```yaml - _scheduling: - affinityClass: dc1 - nodeSelector: - topology.kubernetes.io/zone: dc1 - ``` - -### 5. How Charts Apply Scheduling - -A helper is added to `cozy-lib`: - -```yaml -{{- define "cozy-lib.scheduling.nodeSelector" -}} -{{- if .Values._scheduling }} -{{- if .Values._scheduling.nodeSelector }} -nodeSelector: - {{- .Values._scheduling.nodeSelector | toYaml | nindent 2 }} -{{- end }} -{{- end }} -{{- end -}} -``` - -Each app chart uses the helper when rendering Pod/StatefulSet/Deployment specs: - -```yaml -# packages/apps/postgres/templates/db.yaml -spec: - instances: {{ .Values.replicas }} - {{- include "cozy-lib.scheduling.nodeSelector" . | nindent 2 }} -``` - -```yaml -# packages/apps/redis/templates/redis.yaml -spec: - replicas: {{ .Values.replicas }} - template: - spec: - {{- include "cozy-lib.scheduling.nodeSelector" . | nindent 6 }} -``` - -Charts **must** apply `_scheduling.nodeSelector`. If they don't, pods will be rejected by the webhook. - ---- - -## Validation via Lineage Webhook - -### Why Validation, Not Mutation - -Mutation (injecting nodeSelector into a pod) creates problems: -- Requires merging with existing pod nodeSelector/affinity — complex logic with edge cases -- Operators (CNPG, Strimzi) may overwrite nodeSelector on pod restart -- Hidden behavior: pod is created with one spec but actually runs with another - -Validation is simpler and more reliable: -- Webhook checks: "does this pod **have** the required nodeSelector?" -- If not, the pod is **rejected** with a clear error message -- The chart and operator are responsible for setting the correct spec - -### What Already Exists in the Lineage Webhook - -The lineage webhook (`internal/lineagecontrollerwebhook/webhook.go`) on every Pod creation: - -1. Decodes the Pod -2. Walks the ownership graph (`lineage.WalkOwnershipGraph`) — finds the **owning HelmRelease** -3. Extracts labels from the HelmRelease: `apps.cozystack.io/application.kind`, `.group`, `.name` -4. Applies these labels to the Pod - -**Key point:** the webhook already knows which HelmRelease owns each Pod. - -### What Is Added - -After computing lineage labels, a validation step is added: - -``` -Handle(pod): - 1. [existing] computeLabels(pod) → finds owning HelmRelease - 2. [existing] applyLabels(pod, labels) → mutates labels - 3. [NEW] validateAffinity(pod, hr) → checks nodeSelector - 4. Return patch or Denied -``` - -The `validateAffinity` logic: - -```go -func (h *LineageControllerWebhook) validateAffinity( - ctx context.Context, - pod *unstructured.Unstructured, - hr *helmv2.HelmRelease, -) *admission.Response { - // 1. Extract affinityClass from HelmRelease label - affinityClassName, ok := hr.Labels["apps.cozystack.io/affinity-class"] - if !ok { - return nil // no affinityClass — no validation needed - } - - // 2. Look up AffinityClass from cache - affinityClass, ok := h.affinityClassMap[affinityClassName] - if !ok { - resp := admission.Denied(fmt.Sprintf( - "AffinityClass %q not found", affinityClassName)) - return &resp - } - - // 3. Check pod's nodeSelector - podNodeSelector := extractNodeSelector(pod) // from pod.spec.nodeSelector - for key, expected := range affinityClass.Spec.NodeSelector { - actual, exists := podNodeSelector[key] - if !exists || actual != expected { - resp := admission.Denied(fmt.Sprintf( - "pod %s/%s belongs to application with AffinityClass %q "+ - "but missing required nodeSelector %s=%s", - pod.GetNamespace(), pod.GetName(), - affinityClassName, key, expected)) - return &resp - } - } - - return nil // validation passed -} -``` - -### AffinityClass Caching - -The lineage webhook controller already caches ApplicationDefinitions (`runtimeConfig.appCRDMap`). An AffinityClass cache is added in the same way: - -```go -type runtimeConfig struct { - appCRDMap map[appRef]*cozyv1alpha1.ApplicationDefinition - affinityClassMap map[string]*cozyv1alpha1.AffinityClass // NEW -} -``` - -The controller adds a watch on AffinityClass: - -```go -func (c *LineageControllerWebhook) SetupWithManagerAsController(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&cozyv1alpha1.ApplicationDefinition{}). - Watches(&cozyv1alpha1.AffinityClass{}, &handler.EnqueueRequestForObject{}). - Complete(c) -} -``` - -When an AffinityClass changes, the cache is rebuilt. - ---- - -## End-to-End Flow - -``` -1. Admin creates AffinityClass "dc1" (nodeSelector: zone=dc1) - -2. Admin creates Tenant "acme" (defaultAffinityClass: dc1, allowed: [dc1, dc2]) - → namespace tenant-acme - → cozystack-values Secret with defaultAffinityClass - -3. User creates Postgres "main-db" (affinityClass: dc1) - → API server checks: dc1 ∈ allowedAffinityClasses? ✓ - → API server resolves AffinityClass → nodeSelector - → HelmRelease is created with: - - label: apps.cozystack.io/affinity-class=dc1 - - values: _scheduling.nodeSelector.topology.kubernetes.io/zone=dc1 - -4. FluxCD deploys HelmRelease → Helm renders the chart - → Chart uses cozy-lib helper - → CNPG Cluster is created with nodeSelector: {zone: dc1} - -5. CNPG operator creates Pod - → Pod has nodeSelector: {zone: dc1} - -6. Lineage webhook intercepts the Pod: - a. WalkOwnershipGraph → finds HelmRelease "main-db" - b. HelmRelease label → affinityClass=dc1 - c. AffinityClass "dc1" → nodeSelector: {zone: dc1} - d. Checks: pod.spec.nodeSelector contains zone=dc1? ✓ - e. Admits Pod (+ standard lineage labels) - -7. Scheduler places the Pod on a node in dc1 -``` - -### Error Scenario (chart forgot to apply nodeSelector): - -``` -5. CNPG operator creates Pod WITHOUT nodeSelector - -6. Lineage webhook: - d. Checks: pod.spec.nodeSelector contains zone=dc1? ✗ - e. REJECTS Pod: - "pod main-db-1 belongs to application with AffinityClass dc1 - but missing required nodeSelector topology.kubernetes.io/zone=dc1" - -7. Pod is not created. CNPG operator sees the error and retries. - → Chart developer gets a signal that the chart does not support scheduling. -``` - ---- - -## Code Changes - -### New Files - -| File | Description | -|------------------------------------------------------|-------------------------| -| `api/v1alpha1/affinityclass_types.go` | AffinityClass CRD types | -| `config/crd/bases/cozystack.io_affinityclasses.yaml` | CRD manifest | - -### Modified Files - -| File | Change | -|-------------------------------------------------------|-------------------------------------------------------------------| -| `internal/lineagecontrollerwebhook/webhook.go` | Add `validateAffinity()` to `Handle()` | -| `internal/lineagecontrollerwebhook/config.go` | Add `affinityClassMap` to `runtimeConfig` | -| `internal/lineagecontrollerwebhook/controller.go` | Add watch on AffinityClass | -| `pkg/registry/apps/application/rest.go` | On Create/Update: resolve affinityClass, pass to values and label | -| `packages/apps/tenant/values.yaml` | Add `defaultAffinityClass`, `allowedAffinityClasses` | -| `packages/apps/tenant/templates/namespace.yaml` | Propagate to cozystack-values | -| `packages/system/tenant-rd/cozyrds/tenant.yaml` | Extend OpenAPI schema | -| `packages/library/cozy-lib/templates/_cozyconfig.tpl` | Add `cozy-lib.scheduling.nodeSelector` helper | -| `packages/apps/*/templates/*.yaml` | Each app chart: add helper usage | - ---- - -## Open Questions - -1. **AffinityClass outside Tenants**: Should AffinityClass work for applications outside tenant namespaces (system namespace)? Or only for tenant workloads? - -2. **affinityClass validation on Application creation**: The API server should verify that the specified affinityClass exists and is included in the tenant's `allowedAffinityClasses`. Where should this be done — in the REST handler (`rest.go`) or in a separate validating webhook? - -3. **Soft mode (warn vs deny)**: Is a mode needed where the webhook issues a warning instead of rejecting? This would simplify gradual adoption while not all charts support `_scheduling`. - -4. **affinityClass inheritance**: If a child Tenant does not specify `defaultAffinityClass`, should it be inherited from the parent? The current `cozystack-values` architecture supports this inheritance natively. - -5. **Multiple nodeSelectors**: Is OR-logic support needed (pod can be in dc1 OR dc2)? With `nodeSelector` this is impossible — AffinityClass would need to be extended to `nodeAffinity`. However, validation becomes significantly more complex. diff --git a/examples/backups/vmi/00-helpers.sh b/examples/backups/vmi/00-helpers.sh new file mode 100755 index 00000000..38405495 --- /dev/null +++ b/examples/backups/vmi/00-helpers.sh @@ -0,0 +1,112 @@ +#!/bin/bash +# Helper functions and variables for the VMInstance backup/restore demo +# Source this file in other scripts: source "$(dirname "$0")/00-helpers.sh" + +# ANSI color codes +export RED='\033[0;31m' +export GREEN='\033[0;32m' +export YELLOW='\033[1;33m' +export BLUE='\033[0;34m' +export MAGENTA='\033[0;35m' +export CYAN='\033[0;36m' +export WHITE='\033[1;37m' +export NC='\033[0m' # No Color +export BOLD='\033[1m' + +# Default settings +export NAMESPACE="${NAMESPACE:-tenant-root}" +export BACKUP_STORAGE_LOCATION="${BACKUP_STORAGE_LOCATION:-default}" + +# Logging functions (output to stderr to avoid polluting captured output) +log_info() { + echo -e "${BLUE}ℹ${NC} $*" >&2 +} + +log_success() { + echo -e "${GREEN}✔${NC} $*" >&2 +} + +log_warning() { + echo -e "${YELLOW}⚠${NC} $*" >&2 +} + +log_error() { + echo -e "${RED}✖${NC} $*" >&2 +} + +log_step() { + echo -e "\n${MAGENTA}${BOLD}▶ $*${NC}" >&2 +} + +log_substep() { + echo -e "${CYAN} → $*${NC}" >&2 +} + +log_command() { + echo -e "${WHITE} \$ $*${NC}" >&2 +} + +# Wait for user to press Enter +wait_for_enter() { + echo -e "\n${CYAN}Press Enter to continue...${NC}" >&2 + read -r +} + +# Check if a Kubernetes resource exists +resource_exists() { + local resource_type="$1" + local resource_name="$2" + local namespace="${3:-}" + + if [[ -n "$namespace" ]]; then + kubectl get "$resource_type" "$resource_name" -n "$namespace" &>/dev/null + else + kubectl get "$resource_type" "$resource_name" &>/dev/null + fi +} + +# Wait for a resource field to reach a desired value +wait_for_field() { + local resource_type="$1" + local resource_name="$2" + local jsonpath="$3" + local desired="$4" + local namespace="${5:-}" + local timeout="${6:-300}" + + log_substep "Waiting for $resource_type/$resource_name $jsonpath to become '$desired'..." + + local elapsed=0 + local ns_flag="" + [[ -n "$namespace" ]] && ns_flag="-n $namespace" + + while true; do + local current + # shellcheck disable=SC2086 + current=$(kubectl get "$resource_type" "$resource_name" $ns_flag -o jsonpath="$jsonpath" 2>/dev/null || true) + if [[ "$current" == "$desired" ]]; then + log_success "$resource_type/$resource_name reached '$desired'" + return 0 + fi + if [[ $elapsed -ge $timeout ]]; then + log_error "Timeout waiting for $resource_type/$resource_name (current: '$current', expected: '$desired')" + return 1 + fi + sleep 5 + elapsed=$((elapsed + 5)) + echo -n "." >&2 + done +} + +# Print a separator line +separator() { + echo -e "\n${CYAN}────────────────────────────────────────────────────────────${NC}\n" >&2 +} + +# Print script header +print_header() { + local title="$1" + echo -e "\n${MAGENTA}${BOLD}╔════════════════════════════════════════════════════════════╗${NC}" >&2 + echo -e "${MAGENTA}${BOLD}║${NC} ${WHITE}${BOLD}$title${NC}" >&2 + echo -e "${MAGENTA}${BOLD}╚════════════════════════════════════════════════════════════╝${NC}\n" >&2 +} diff --git a/examples/backups/vmi/01-create-strategies.sh b/examples/backups/vmi/01-create-strategies.sh new file mode 100755 index 00000000..78aa34d3 --- /dev/null +++ b/examples/backups/vmi/01-create-strategies.sh @@ -0,0 +1,145 @@ +#!/bin/bash +# Step 01: Create Velero backup/restore strategies for VMInstance and VMDisk +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/00-helpers.sh" + +print_header "Step 1: Create Velero Strategies" + +log_step "Creating VMInstance strategy..." +log_command "kubectl apply -f - (Velero strategy: vminstance-strategy)" + +kubectl apply -f - <<'EOF' +apiVersion: strategy.backups.cozystack.io/v1alpha1 +kind: Velero +metadata: + name: vminstance-strategy +spec: + template: + # Symmetric restore filters: kubevirt-velero-plugin requires launcher pods in the backup, + # but restore orLabelSelectors limit what is applied from the tarball (e.g. skip another + # VM's pod that Velero added via PVC item action). VMDisk OR branches are appended by + # the controller from backup.status.underlyingResources or the Velero backup annotation. + restoreSpec: + existingResourcePolicy: update + includedNamespaces: + - '{{ .Application.metadata.namespace }}' + orLabelSelectors: + - matchLabels: + app.kubernetes.io/instance: 'vm-instance-{{ .Application.metadata.name }}' + - matchLabels: + apps.cozystack.io/application.kind: '{{ .Application.kind }}' + apps.cozystack.io/application.name: '{{ .Application.metadata.name }}' + includedResources: + - helmreleases.helm.toolkit.fluxcd.io + - virtualmachines.kubevirt.io + - virtualmachineinstances.kubevirt.io + - pods + - persistentvolumeclaims + - configmaps + - secrets + - controllerrevisions.apps + includeClusterResources: false + excludedResources: + # Required to avoid conflict with restored DV from HR VMDisk + - datavolumes.cdi.kubevirt.io + + spec: # see https://velero.io/docs/v1.17/api-types/backup/ + includedNamespaces: + - '{{ .Application.metadata.namespace }}' + orLabelSelectors: + # VM resources (VirtualMachine, DataVolume, PVC, etc.) + - matchLabels: + app.kubernetes.io/instance: 'vm-instance-{{ .Application.metadata.name }}' + # HelmRelease (the Cozystack app object) + - matchLabels: + apps.cozystack.io/application.kind: '{{ .Application.kind }}' + apps.cozystack.io/application.name: '{{ .Application.metadata.name }}' + includedResources: + - helmreleases.helm.toolkit.fluxcd.io + - virtualmachines.kubevirt.io + - virtualmachineinstances.kubevirt.io + # Required by kubevirt-velero-plugin for running VMs ("launcher pod must be in backup"). + - pods + # Required by kubevirt-velero-plugin requires DV to be in backup of VM, but it excludes in restores + - datavolumes.cdi.kubevirt.io + - persistentvolumeclaims + - configmaps + - secrets + - controllerrevisions.apps + includeClusterResources: false + storageLocation: '{{ .Parameters.backupStorageLocationName }}' + volumeSnapshotLocations: + - '{{ .Parameters.backupStorageLocationName }}' + snapshotVolumes: true + snapshotMoveData: true + ttl: 720h0m0s + itemOperationTimeout: 24h0m0s +EOF + +log_success "VMInstance strategy created" + +separator + +log_step "Creating VMDisk strategy..." +log_command "kubectl apply -f - (Velero strategy: vmdisk-strategy)" + +kubectl apply -f - <<'EOF' +apiVersion: strategy.backups.cozystack.io/v1alpha1 +kind: Velero +metadata: + name: vmdisk-strategy +spec: + template: + restoreSpec: + existingResourcePolicy: update + includedNamespaces: + - '{{ .Application.metadata.namespace }}' + orLabelSelectors: + - matchLabels: + app.kubernetes.io/instance: 'vm-disk-{{ .Application.metadata.name }}' + - matchLabels: + apps.cozystack.io/application.kind: '{{ .Application.kind }}' + apps.cozystack.io/application.name: '{{ .Application.metadata.name }}' + includedResources: + - helmreleases.helm.toolkit.fluxcd.io + - persistentvolumeclaims + - configmaps + includeClusterResources: false + + spec: + includedNamespaces: + - '{{ .Application.metadata.namespace }}' + orLabelSelectors: + - matchLabels: + app.kubernetes.io/instance: 'vm-disk-{{ .Application.metadata.name }}' + - matchLabels: + apps.cozystack.io/application.kind: '{{ .Application.kind }}' + apps.cozystack.io/application.name: '{{ .Application.metadata.name }}' + includedResources: + - helmreleases.helm.toolkit.fluxcd.io + - persistentvolumeclaims + - configmaps + includeClusterResources: false + storageLocation: '{{ .Parameters.backupStorageLocationName }}' + volumeSnapshotLocations: + - '{{ .Parameters.backupStorageLocationName }}' + snapshotVolumes: true + snapshotMoveData: true + ttl: 720h0m0s + itemOperationTimeout: 24h0m0s +EOF + +log_success "VMDisk strategy created" + +separator + +log_step "Verifying strategies..." +log_command "kubectl get velero.strategy.backups.cozystack.io" +kubectl get velero.strategy.backups.cozystack.io + +separator + +log_success "Velero strategies are ready" +echo -e "\n${GREEN}${BOLD}Next step:${NC} ./02-create-backupclass.sh" diff --git a/examples/backups/vmi/02-create-backupclass.sh b/examples/backups/vmi/02-create-backupclass.sh new file mode 100755 index 00000000..08c29fc1 --- /dev/null +++ b/examples/backups/vmi/02-create-backupclass.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# Step 02: Create BackupClass that binds strategies to application types +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/00-helpers.sh" + +print_header "Step 2: Create BackupClass" + +log_step "Creating BackupClass 'velero'..." +log_info "BackupClass maps application kinds (VMInstance, VMDisk) to their Velero strategies" +log_command "kubectl apply -f - (BackupClass: velero)" + +kubectl apply -f - < + external: false + externalMethod: PortList + externalPorts: + - 22 +EOF + +log_success "VMInstance created" + +separator + +log_step "Verifying VMInstance..." +log_command "kubectl get vminstance test -n $NAMESPACE" +kubectl get vminstance test -n "$NAMESPACE" + +separator + +log_success "VMInstance is ready" +echo -e "\n${GREEN}${BOLD}Next step:${NC} ./05-create-backupjob.sh" diff --git a/examples/backups/vmi/05-create-backupjob.sh b/examples/backups/vmi/05-create-backupjob.sh new file mode 100755 index 00000000..581490a4 --- /dev/null +++ b/examples/backups/vmi/05-create-backupjob.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Step 05: Create a BackupJob to back up the VMInstance +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/00-helpers.sh" + +print_header "Step 5: Create BackupJob" + +log_step "Creating BackupJob 'test-backup' in namespace $NAMESPACE..." +log_info "This triggers a Velero backup of VMInstance 'test' and all its disks" +log_command "kubectl apply -f - (BackupJob: test-backup)" + +kubectl apply -f - <-orig-`, only for in-place restore + keepOriginalIpAndMac: true # restores original IP and MAC address of VMI via OVN annotations +EOF + +log_success "RestoreJob created" + +separator + +log_step "Waiting for RestoreJob to complete..." +wait_for_field restorejob restore-in-place-test '{.status.phase}' Succeeded "$NAMESPACE" 600 + +separator + +log_step "Verifying RestoreJob result..." +log_command "kubectl get restorejob restore-in-place-test -n $NAMESPACE -o yaml" +kubectl get restorejob restore-in-place-test -n "$NAMESPACE" -o wide + +separator + +log_success "In-place restore completed successfully" +echo -e "\n${GREEN}${BOLD}Next step:${NC} ./07-restore-to-copy.sh" diff --git a/examples/backups/vmi/07-restore-to-copy.sh b/examples/backups/vmi/07-restore-to-copy.sh new file mode 100755 index 00000000..9b84ce55 --- /dev/null +++ b/examples/backups/vmi/07-restore-to-copy.sh @@ -0,0 +1,68 @@ +#!/bin/bash +# Step 07: Restore the VMInstance to a copy in a different namespace +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/00-helpers.sh" + +TARGET_NAMESPACE="${TARGET_NAMESPACE:-tenant-root-copy}" + +print_header "Step 7: Restore VMInstance to Copy (Cross-Namespace)" + +log_info "Restoring to the same namespace with a different app name is not supported" +log_info "due to Velero DataUpload limitations. Cross-namespace restore uses Velero's namespaceMapping." + +separator + +log_step "Ensuring target namespace '$TARGET_NAMESPACE' exists..." +kubectl create namespace "$TARGET_NAMESPACE" --dry-run=client -o yaml | kubectl apply -f - +log_success "Namespace '$TARGET_NAMESPACE' is ready" + +separator + +log_step "Creating RestoreJob 'restore-to-copy-test' in namespace $NAMESPACE..." +log_info "The backup will be restored into namespace '$TARGET_NAMESPACE' using Velero namespaceMapping" +log_command "kubectl apply -f - (RestoreJob: restore-to-copy-test)" + +kubectl apply -f - <-orig-`, only for in-place restore + keepOriginalIpAndMac: false # restores original IP and MAC address of VMI via OVN annotations +EOF + +log_success "RestoreJob created" + +separator + +log_step "Waiting for RestoreJob to complete..." +wait_for_field restorejob restore-to-copy-test '{.status.phase}' Succeeded "$NAMESPACE" 600 + +separator + +log_step "Verifying RestoreJob result..." +log_command "kubectl get restorejob restore-to-copy-test -n $NAMESPACE -o yaml" +kubectl get restorejob restore-to-copy-test -n "$NAMESPACE" -o wide + +separator + +log_step "Checking resources in target namespace..." +log_command "kubectl get all -n $TARGET_NAMESPACE" +kubectl get all -n "$TARGET_NAMESPACE" 2>/dev/null || log_warning "No resources found in $TARGET_NAMESPACE" + +separator + +log_success "Cross-namespace restore completed successfully" diff --git a/examples/backups/vmi/cleanup.sh b/examples/backups/vmi/cleanup.sh new file mode 100755 index 00000000..5965c3f0 --- /dev/null +++ b/examples/backups/vmi/cleanup.sh @@ -0,0 +1,70 @@ +#!/bin/bash +# Clean up all resources created by the demo +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/00-helpers.sh" + +TARGET_NAMESPACE="${TARGET_NAMESPACE:-tenant-root-copy}" + +print_header "Cleanup Demo Resources" + +log_warning "This script will delete all resources created during the demo" +echo -e "\n${YELLOW}The following will be deleted:${NC}" +echo " - RestoreJobs (restore-in-place-test, restore-to-copy-test)" +echo " - BackupJob (test-backup) and associated Backup" +echo " - VMInstance (test)" +echo " - VMDisk (ubuntu-source)" +echo " - BackupClass (velero)" +echo " - Velero strategies (vminstance-strategy, vmdisk-strategy)" +echo " - Target namespace ($TARGET_NAMESPACE)" +echo "" + +read -p "Continue? (y/N): " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + log_info "Cancelled" + exit 0 +fi + +separator + +log_step "Deleting RestoreJobs..." +kubectl delete restorejob restore-to-copy-test -n "$NAMESPACE" 2>/dev/null || log_warning "RestoreJob restore-to-copy-test not found" +kubectl delete restorejob restore-in-place-test -n "$NAMESPACE" 2>/dev/null || log_warning "RestoreJob restore-in-place-test not found" + +separator + +log_step "Deleting BackupJob and Backup..." +kubectl delete backupjob test-backup -n "$NAMESPACE" 2>/dev/null || log_warning "BackupJob test-backup not found" +kubectl delete backup test-backup -n "$NAMESPACE" 2>/dev/null || log_warning "Backup test-backup not found" + +separator + +log_step "Deleting VMInstance..." +kubectl delete vminstance test -n "$NAMESPACE" 2>/dev/null || log_warning "VMInstance test not found" + +log_step "Deleting VMDisk..." +kubectl delete vmdisk ubuntu-source -n "$NAMESPACE" 2>/dev/null || log_warning "VMDisk ubuntu-source not found" + +separator + +log_step "Deleting BackupClass..." +kubectl delete backupclass velero 2>/dev/null || log_warning "BackupClass velero not found" + +separator + +log_step "Deleting Velero strategies..." +kubectl delete velero.strategy.backups.cozystack.io vminstance-strategy 2>/dev/null || log_warning "Strategy vminstance-strategy not found" +kubectl delete velero.strategy.backups.cozystack.io vmdisk-strategy 2>/dev/null || log_warning "Strategy vmdisk-strategy not found" + +separator + +log_step "Deleting target namespace..." +kubectl delete namespace "$TARGET_NAMESPACE" 2>/dev/null || log_warning "Namespace $TARGET_NAMESPACE not found" + +separator + +log_success "Cleanup complete" +log_info "All demo resources have been deleted" +echo -e "\n${GREEN}${BOLD}To re-run the demo:${NC} ./01-create-strategies.sh" diff --git a/examples/backups/vmi/run-all.sh b/examples/backups/vmi/run-all.sh new file mode 100755 index 00000000..b58f4b9d --- /dev/null +++ b/examples/backups/vmi/run-all.sh @@ -0,0 +1,62 @@ +#!/bin/bash +# Run the full VMInstance backup/restore demo sequentially +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/00-helpers.sh" + +print_header "VMInstance Backup & Restore Demo" + +log_info "This script will run all demo steps sequentially" +log_info "There will be a pause between steps for observation" +echo "" + +SCRIPTS=( + "01-create-strategies.sh" + "02-create-backupclass.sh" + "03-create-vmdisk.sh" + "04-create-vminstance.sh" + "05-create-backupjob.sh" + "06-restore-in-place.sh" + "07-restore-to-copy.sh" +) + +echo -e "${CYAN}Demo steps:${NC}" +for i in "${!SCRIPTS[@]}"; do + echo " $((i+1)). ${SCRIPTS[$i]}" +done +echo "" + +read -p "Run demo? (y/N): " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + log_info "Cancelled" + exit 0 +fi + +separator + +for script in "${SCRIPTS[@]}"; do + if [[ -x "$SCRIPT_DIR/$script" ]]; then + "$SCRIPT_DIR/$script" + separator + wait_for_enter + else + log_error "Script not found or not executable: $script" + exit 1 + fi +done + +print_header "Demo Complete" + +log_success "All steps completed successfully!" +echo "" +log_info "What was demonstrated:" +echo " 1. Created Velero backup strategies for VMInstance and VMDisk" +echo " 2. Created BackupClass binding strategies to application types" +echo " 3. Provisioned a VMDisk and VMInstance" +echo " 4. Created a backup of the VMInstance via BackupJob" +echo " 5. Restored the VMInstance in-place" +echo " 6. Restored the VMInstance to a copy in a different namespace" +echo "" +log_info "To clean up resources: ./cleanup.sh" diff --git a/examples/backups/vmi/scenario-admin-prepare.md b/examples/backups/vmi/scenario-admin-prepare.md new file mode 100644 index 00000000..edfca5d1 --- /dev/null +++ b/examples/backups/vmi/scenario-admin-prepare.md @@ -0,0 +1,73 @@ +# Scenario: Cluster Administrator Configures Backups + +This scenario walks through the cluster-level setup required before users can +back up and restore their virtual machines. A cluster administrator creates +Velero strategies and a BackupClass that binds those strategies to Cozystack +application types. + +## Prerequisites + +- A running Cozystack cluster with the backup-controller installed. +- Velero deployed in the `cozy-velero` namespace with a configured + BackupStorageLocation (default: `default`). +- `kubectl` access with cluster-admin privileges. + +## Steps + +### 1. Create Velero Strategies + +```bash +./01-create-strategies.sh +``` + +This creates two cluster-scoped `Velero` strategy objects: + +| Strategy | Purpose | +|---|---| +| `vminstance-strategy` | Defines backup and restore templates for `VMInstance` applications. Includes VirtualMachine, PVCs, HelmReleases, pods (required by kubevirt-velero-plugin), and supporting resources. Uses `snapshotMoveData: true` for portable volume snapshots. | +| `vmdisk-strategy` | Defines backup and restore templates for standalone `VMDisk` applications. Covers HelmReleases, PVCs, and ConfigMaps. | + +Both strategies use Go templates with `{{ .Application.metadata.name }}`, +`{{ .Application.metadata.namespace }}`, and `{{ .Parameters.backupStorageLocationName }}` +so they can be reused across any application instance. + +**Key design details:** + +- `orLabelSelectors` scope backups to only the resources belonging to a + specific application, preventing cross-contamination between VMs in the + same namespace. +- The restore spec excludes `datavolumes.cdi.kubevirt.io` to avoid conflicts + when the HelmRelease of VMDisk recreates DataVolumes after restore. +- `existingResourcePolicy: update` allows restoring over existing resources + during in-place restore. + +### 2. Create BackupClass + +```bash +./02-create-backupclass.sh +``` + +Creates a `BackupClass` named `velero` that maps application types to their +strategies: + +| Application Kind | Strategy | Parameters | +|---|---|---| +| `VMInstance` (`apps.cozystack.io`) | `vminstance-strategy` | `backupStorageLocationName: default` | +| `VMDisk` (`apps.cozystack.io`) | `vmdisk-strategy` | `backupStorageLocationName: default` | + +The `backupStorageLocationName` parameter can be overridden via the +`BACKUP_STORAGE_LOCATION` environment variable if your Velero installation +uses a non-default storage location. + +## Configuration + +| Variable | Default | Description | +|---|---|---| +| `BACKUP_STORAGE_LOCATION` | `default` | Velero BackupStorageLocation name | + +## Result + +After completing these steps the cluster is ready for users to create backups +of their `VMInstance` and `VMDisk` applications by referencing the `velero` +BackupClass. No further admin action is required for individual backup or +restore operations. diff --git a/examples/backups/vmi/scenario-user-backup.md b/examples/backups/vmi/scenario-user-backup.md new file mode 100644 index 00000000..31241fd9 --- /dev/null +++ b/examples/backups/vmi/scenario-user-backup.md @@ -0,0 +1,91 @@ +# Scenario: User Creates a VM Backup + +This scenario demonstrates how a tenant user provisions a virtual machine and +creates a backup of it. The backup captures the VM definition, its disks, and +network identity so it can be restored later. + +## Prerequisites + +- Cluster administrator has completed the setup from + [scenario-admin.md](scenario-admin.md) (strategies and BackupClass exist). +- A tenant namespace (default: `tenant-root`). +- `kubectl` access scoped to the tenant namespace. + +## Steps + +### 1. Create a VMDisk + +```bash +./03-create-vmdisk.sh +``` + +Creates a `VMDisk` named `ubuntu-source` that downloads the Ubuntu Noble cloud +image and provisions a 20Gi replicated PVC. The disk serves as the boot volume +for the virtual machine. + +Wait for the image download to complete before proceeding. You can monitor +progress with: + +```bash +kubectl get vmdisk ubuntu-source -n tenant-root -w +``` + +### 2. Create a VMInstance + +```bash +./04-create-vminstance.sh +``` + +Creates a `VMInstance` named `test` that references the `ubuntu-source` disk. +The VM boots with the `ubuntu` instance profile on a `u1.medium` instance type. + +Verify the VM is running: + +```bash +kubectl get vmi -n tenant-root +``` + +### 3. Create a BackupJob + +```bash +./05-create-backupjob.sh +``` + +Creates a `BackupJob` named `test-backup` that triggers a full backup of the +`test` VMInstance. The backup controller: + +1. Resolves the `velero` BackupClass to find the matching `vminstance-strategy`. +2. Discovers underlying resources (DataVolumes, OVN IP/MAC from the VM pod). +3. Creates a Velero Backup in the `cozy-velero` namespace with label selectors + scoped to this specific VM and its disks. +4. Velero snapshots and moves the volume data to the configured storage + location. + +The script waits up to 10 minutes for the BackupJob to reach the `Succeeded` +phase. On completion, a `Backup` object is created in the same namespace +containing: + +- `spec.applicationRef` — reference to the backed-up VMInstance. +- `spec.strategyRef` — reference to the Velero strategy used. +- `spec.driverMetadata` — Velero backup name for later restore. +- `status.underlyingResources` — captured DataVolume names and OVN IP/MAC + addresses. + +You can inspect the resulting Backup: + +```bash +kubectl get backups -n tenant-root +kubectl get backup test-backup -n tenant-root -o yaml +``` + +## Configuration + +| Variable | Default | Description | +|---|---|---| +| `NAMESPACE` | `tenant-root` | Tenant namespace for all resources | + +## Result + +After completing these steps you have a `Backup` artifact that can be used to +restore the VM either in-place or to a different namespace. See +[scenario-user-restore.md](scenario-user-restore.md) for restore options. diff --git a/examples/backups/vmi/scenario-user-restore.md b/examples/backups/vmi/scenario-user-restore.md new file mode 100644 index 00000000..1d35b22b --- /dev/null +++ b/examples/backups/vmi/scenario-user-restore.md @@ -0,0 +1,101 @@ +# Scenario: User Restores a VM + +This scenario demonstrates two restore methods: in-place restore (rollback the +VM to a previous state) and cross-namespace restore (create a copy of the VM in +a different namespace). + +## Prerequisites + +- A completed backup from [scenario-user-backup.md](scenario-user-backup.md) + (the `test-backup` Backup object exists in the tenant namespace). +- `kubectl` access scoped to the tenant namespace. + +## Method 1: In-Place Restore + +```bash +./06-restore-in-place.sh +``` + +Creates a `RestoreJob` that restores the `test` VMInstance back to the state +captured in the `test-backup` Backup. The `targetApplicationRef` points to the +same application as the original backup. + +The backup controller performs the following steps automatically: + +1. **Suspends HelmReleases** — prevents Flux from reconciling the VM and its + disks during restore. +2. **Halts the VirtualMachine** — sets `runStrategy: Halted` and waits for the + VirtualMachineInstance (launcher pod) to terminate. +3. **Renames existing PVCs** — moves each PVC to `-orig-` to + preserve the current disk data as a safety net. +4. **Deletes DataVolumes** — removes DVs so CDI does not recreate PVCs before + Velero restores them. +5. **Creates Velero Restore** — restores resources from the backup with: + - `existingResourcePolicy: update` to overwrite existing objects. + - Resource modifier rules that add `cdi.kubevirt.io/allowClaimAdoption=true` + to restored PVCs so CDI can adopt them. + - OVN IP/MAC annotations to preserve the VM's network identity. + +After the Velero Restore completes, the HelmReleases resume and the VM boots +with the restored disk data while retaining its original IP and MAC addresses. + +## Method 2: Cross-Namespace Restore (Copy) + +```bash +./07-restore-to-copy.sh +``` + +Creates a `RestoreJob` with `spec.options.targetNamespace` set to a different namespace +(default: `tenant-root-copy`). This creates an independent copy of the VM +without affecting the original. + +Key differences from in-place restore: + +| Aspect | In-Place | Cross-Namespace | +|---|---|---| +| Source VM affected | Yes (halted, PVCs renamed) | No (untouched) | +| Velero namespaceMapping | Not used | Maps source NS to target NS | +| OVN IP/MAC | Preserved from backup | Skipped (new IP/MAC assigned) | +| Pre-restore preparation | Full (suspend, halt, rename, delete) | Skipped | + +The script ensures the target namespace exists before creating the RestoreJob. +Velero's `namespaceMapping` redirects all resources from the source namespace to +the target namespace during restore. + +### Important limitation + +Restoring to the **same namespace** with a **different application name** is not +supported. Velero's DataUpload always writes volume data to PVCs with the +original name regardless of resource modifiers, which causes conflicts. If you +attempt this, the RestoreJob will fail with an error message suggesting +cross-namespace restore instead. + +## Configuration + +| Variable | Default | Description | +|---|---|---| +| `NAMESPACE` | `tenant-root` | Source namespace (where the Backup lives) | +| `TARGET_NAMESPACE` | `tenant-root-copy` | Target namespace for cross-namespace restore | + +## Verify + +After either restore method, verify the VM is running: + +```bash +# In-place +kubectl get vmi -n tenant-root + +# Cross-namespace copy +kubectl get vmi -n tenant-root-copy +``` + +## Cleanup + +To remove all resources created by the demo: + +```bash +./cleanup.sh +``` + +This deletes RestoreJobs, BackupJobs, Backups, VMInstance, VMDisk, BackupClass, +strategies, and the target namespace. diff --git a/examples/desired-backup.yaml b/examples/desired-backup.yaml deleted file mode 100644 index c06b8f82..00000000 --- a/examples/desired-backup.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: backups.cozystack.io/v1alpha1 -kind: BackupJob -metadata: - name: desired-backup - namespace: tenant-root - labels: - backups.cozystack.io/triggered-by: manual -spec: - applicationRef: - apiGroup: apps.cozystack.io - kind: VirtualMachine - name: vm1 - storageRef: - apiGroup: apps.cozystack.io - kind: Bucket - name: test-bucket - strategyRef: - apiGroup: strategy.backups.cozystack.io - kind: Velero - name: velero-strategy-default \ No newline at end of file diff --git a/go.mod b/go.mod index 3ecda336..7b7d5bc4 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ module github.com/cozystack/cozystack go 1.25.0 require ( + github.com/cozystack/cozystack-scheduler/pkg/apis v0.1.1 github.com/emicklei/dot v1.10.0 github.com/fluxcd/helm-controller/api v1.4.3 github.com/fluxcd/source-controller/api v1.7.4 @@ -28,8 +29,10 @@ require ( k8s.io/klog/v2 v2.130.1 k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d + sigs.k8s.io/container-object-storage-interface-api v0.1.0 sigs.k8s.io/controller-runtime v0.22.4 sigs.k8s.io/structured-merge-diff/v6 v6.3.0 + sigs.k8s.io/yaml v1.6.0 ) require ( @@ -44,7 +47,6 @@ require ( github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.12.2 // indirect - github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fluxcd/pkg/apis/acl v0.9.0 // indirect @@ -125,7 +127,6 @@ require ( sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/yaml v1.6.0 // indirect ) // See: issues.k8s.io/135537 diff --git a/go.sum b/go.sum index 0a69ac87..81e1779f 100644 --- a/go.sum +++ b/go.sum @@ -20,6 +20,8 @@ github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8 github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cozystack/apimachinery v0.0.0-20251219010959-1f91eabae46c h1:C2wIfH/OzhU9XOK/e6Ik9cg7nZ1z6fN4lf6a3yFdik8= github.com/cozystack/apimachinery v0.0.0-20251219010959-1f91eabae46c/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +github.com/cozystack/cozystack-scheduler/pkg/apis v0.1.1 h1:9uLa/8J4lRx3sNuaVH8UZqgZvnskHATPo5GxEKh0iSM= +github.com/cozystack/cozystack-scheduler/pkg/apis v0.1.1/go.mod h1:kPeS9YPB4ENbvNINEkkp0SX8FB+gvwoOHGOHMT3Tg9Y= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -319,6 +321,8 @@ k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d h1:wAhiDyZ4Tdtt7e46e9M5ZSAJ/MnPG k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= +sigs.k8s.io/container-object-storage-interface-api v0.1.0 h1:8tB6JFQhbQIC1hwGQ+q4+tmSSNfjKemb7bFI6C0CK/4= +sigs.k8s.io/container-object-storage-interface-api v0.1.0/go.mod h1:YiB+i/UGkzqgODDhRG3u7jkbWkQcoUeLEJ7hwOT/2Qk= sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A= sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= diff --git a/hack/admin-kubeconfig-invariant.bats b/hack/admin-kubeconfig-invariant.bats new file mode 100644 index 00000000..a4b98b00 --- /dev/null +++ b/hack/admin-kubeconfig-invariant.bats @@ -0,0 +1,145 @@ +#!/usr/bin/env bats +# ----------------------------------------------------------------------------- +# Chart-wide invariant for packages/apps/kubernetes: +# +# Every Deployment in this chart that mounts -admin-kubeconfig as a +# Secret volume MUST: +# - declare that volume optional: true (so kubelet does not FailedMount +# while Kamaji is still provisioning the Secret), AND +# - include the wait-for-kubeconfig init container (so the pod becomes +# Ready only after Kamaji publishes the Secret). +# +# The per-template unittests in packages/apps/kubernetes/tests/ lock in +# today's three Deployments (cluster-autoscaler, kccm, csi controller) by +# name. This invariant is stricter: any future Deployment added to this +# chart that mounts the same Secret but forgets the guard will fail here. +# +# Requires: helm, yq (mikefarah v4+), jq. All three are available on the +# project's CI runners and on the maintainer workstation. +# ----------------------------------------------------------------------------- + +@test "every Deployment mounting admin-kubeconfig has optional:true and wait-for-kubeconfig init" { + values_file="packages/apps/kubernetes/tests/values-ci.yaml" + [ -f "$values_file" ] + + tmp=$(mktemp -d) + trap 'rm -rf "$tmp"' EXIT + + helm template invariant packages/apps/kubernetes \ + --namespace tenant-root \ + --values "$values_file" \ + 2>/dev/null > "$tmp/rendered.yaml" + + # yq streams one JSON object per input document. jq -s slurps the stream + # into an array so we can treat all Deployments as a single collection. + yq --output-format=json eval-all '.' "$tmp/rendered.yaml" \ + | jq -s --raw-output ' + map(select(.kind == "Deployment")) | + map({ + name: .metadata.name, + volumes: (.spec.template.spec.volumes // []), + initNames: ((.spec.template.spec.initContainers // []) | map(.name)), + }) | + map( + .name as $n | + .initNames as $ins | + (.volumes[] | select(.secret.secretName | test("-admin-kubeconfig$")?)) + | { + name: $n, + optional: (.secret.optional == true), + hasInit: ($ins | index("wait-for-kubeconfig") != null), + } + ) + ' > "$tmp/summary.json" + + # At least one Deployment must match; if a refactor removes every + # admin-kubeconfig volume from this chart, the test must be updated + # deliberately rather than silently passing. + matched=$(jq 'length' "$tmp/summary.json") + [ "$matched" -ge 1 ] + + offenders=$(jq --raw-output '.[] | select(.optional != true or .hasInit != true) | .name' "$tmp/summary.json") + + if [ -n "$offenders" ]; then + echo "Deployments mounting *-admin-kubeconfig without optional:true + wait-for-kubeconfig init:" >&2 + echo "$offenders" >&2 + echo "Full summary:" >&2 + cat "$tmp/summary.json" >&2 + exit 1 + fi + + echo "Invariant holds for $matched Deployment(s)" +} + +@test "chart emits zero admin-kubeconfig Deployments when tenant has no etcd DataStore" { + # Without a DataStore (parent Tenant has not populated _namespace.etcd yet) + # the control-plane-side Deployments must NOT render at all. If they did, + # the wait-for-kubeconfig init would CrashLoopBackOff indefinitely - there + # would be no KamajiControlPlane to provision the Secret - consuming the + # HelmRelease wait budget and triggering exactly the remediation cycle the + # rest of this chart tries to avoid. This test renders the whole chart + # with etcd empty and asserts no Deployment references the admin-kubeconfig + # Secret. + tmp=$(mktemp -d) + trap 'rm -rf "$tmp"' EXIT + + helm template invariant packages/apps/kubernetes \ + --namespace tenant-root \ + --values packages/apps/kubernetes/tests/values-ci-no-etcd.yaml \ + 2>/dev/null > "$tmp/rendered.yaml" + + matched=$( + yq --output-format=json eval-all '.' "$tmp/rendered.yaml" \ + | jq -s ' + map(select(.kind == "Deployment")) | + map(select( + (.spec.template.spec.volumes // []) + | any(.secret.secretName | test("-admin-kubeconfig$")?) + )) | + length + ' + ) + + if [ "$matched" -ne 0 ]; then + echo "Expected zero Deployments mounting *-admin-kubeconfig when etcd is empty, got $matched:" >&2 + yq --output-format=json eval-all '.' "$tmp/rendered.yaml" \ + | jq -s 'map(select(.kind == "Deployment") | .metadata.name)' >&2 + exit 1 + fi + + echo "No admin-kubeconfig Deployments rendered for empty etcd (as expected)" +} + +@test "chart emits zero admin-kubeconfig HelmReleases when tenant has no etcd DataStore" { + # Same principle as the Deployment variant above, extended to every child + # HelmRelease (cilium, coredns, csi, cert-manager, ...). They reference + # *-admin-kubeconfig via kubeConfig.secretRef and would otherwise sit in + # NotReady forever on an etcd-less tenant, polluting the HelmRelease list + # the operator sees and contradicting the "awaiting-etcd beacon only" + # contract of the soft-skip path. + tmp=$(mktemp -d) + trap 'rm -rf "$tmp"' EXIT + + helm template invariant packages/apps/kubernetes \ + --namespace tenant-root \ + --values packages/apps/kubernetes/tests/values-ci-no-etcd.yaml \ + 2>/dev/null > "$tmp/rendered.yaml" + + matched=$( + yq --output-format=json eval-all '.' "$tmp/rendered.yaml" \ + | jq -s ' + map(select(.kind == "HelmRelease")) | + map(select(.spec.kubeConfig.secretRef.name | test("-admin-kubeconfig$")?)) | + length + ' + ) + + if [ "$matched" -ne 0 ]; then + echo "Expected zero HelmReleases referencing *-admin-kubeconfig when etcd is empty, got $matched:" >&2 + yq --output-format=json eval-all '.' "$tmp/rendered.yaml" \ + | jq -s 'map(select(.kind == "HelmRelease") | .metadata.name)' >&2 + exit 1 + fi + + echo "No admin-kubeconfig HelmReleases rendered for empty etcd (as expected)" +} diff --git a/hack/cdi_golden_image_create.sh b/hack/cdi_golden_image_create.sh index be1558d0..f19f9970 100644 --- a/hack/cdi_golden_image_create.sh +++ b/hack/cdi_golden_image_create.sh @@ -16,7 +16,7 @@ kubectl create -f - <:(:)+ +# where each is [a-z0-9_]. Raw DCGM metrics (DCGM_FI_*), +# kube-state-metrics (kube_*) and similar uppercase / single-word metric +# names do not match because the leading segment must be lowercase and the +# whole expression must contain at least two ':' characters. +extract_refs() { + json_file=$1 + # Prometheus convention allows 2-segment rule names (level:metric); this + # regex is tuned to the 3+ segment convention used in this repo + # (level:metric:op — e.g. cluster:gpu_count:total). Update if future + # rules use 2 segments, otherwise they will be silently skipped. + grep -hoE '[a-z][a-z0-9_]*:[a-z0-9_]+:[a-z0-9_]+' "$json_file" | sort -u +} + +# Resolve "gpu/foo" -> "$DASHBOARDS_DIR/gpu/foo.json" +list_tracked_gpu_dashboards() { + awk '/^gpu\// { print $0 ".json" }' "$DASHBOARDS_LIST" +} + +# Extract the set of DCGM_FI_* metric names declared in a CSV file. Handles +# both the upstream-style default CSV (unindented) and the ConfigMap-style +# custom CSV (YAML-indented). A declaration line starts — after any leading +# whitespace — with "DCGM_FI_," ; comment lines begin with "#" and are +# skipped. Uses POSIX awk's match()+RSTART/RLENGTH so no GNU extensions +# are required. +extract_csv_metrics() { + file=$1 + awk ' + { + line = $0 + sub(/^[[:space:]]+/, "", line) + if (line ~ /^#/) next + if (match(line, /^DCGM_FI_[A-Z0-9_]+/)) { + print substr(line, RSTART, RLENGTH) + } + } + ' "$file" | sort -u +} + +# Extract the set of DCGM_FI_* metric references from a text file (dashboard +# JSON or rules YAML). A DCGM metric name has at least two underscore-delimited +# segments after the "DCGM_FI_" prefix (e.g. DCGM_FI_DEV_GPU_UTIL, DCGM_FI_PROF_ +# PIPE_TENSOR_ACTIVE, DCGM_FI_DRIVER_VERSION). Requiring two segments keeps +# the matcher from latching onto glob stubs like "DCGM_FI_DEV_*_VIOLATION" that +# appear in comments. +extract_dcgm_refs() { + file=$1 + grep -hoE 'DCGM_FI_[A-Z0-9]+(_[A-Z0-9]+)+' "$file" | sort -u +} + +@test "every recording rule reference in tracked GPU dashboards has a matching record" { + TMP=$(mktemp -d) + trap 'rm -rf "$TMP"' EXIT + + extract_rules > "$TMP/rules.txt" + [ -s "$TMP/rules.txt" ] || { echo "no recording rules extracted from $RULES_FILE" >&2; exit 1; } + + list_tracked_gpu_dashboards > "$TMP/dashboards.txt" + [ -s "$TMP/dashboards.txt" ] || { echo "no gpu/* dashboards listed in $DASHBOARDS_LIST" >&2; exit 1; } + + failed=0 + while IFS= read -r dashboard_rel; do + dashboard="$DASHBOARDS_DIR/$dashboard_rel" + if [ ! -f "$dashboard" ]; then + echo "ERROR: dashboard listed but file missing: $dashboard" >&2 + failed=1 + continue + fi + + extract_refs "$dashboard" > "$TMP/refs.txt" + # comm -23: lines unique to refs.txt (referenced but not defined) + # Both inputs must be sorted; extract_* helpers already sort. + comm -23 "$TMP/refs.txt" "$TMP/rules.txt" > "$TMP/missing.txt" + if [ -s "$TMP/missing.txt" ]; then + echo "ERROR: $dashboard_rel references undefined recording rules:" >&2 + sed 's/^/ - /' "$TMP/missing.txt" >&2 + failed=1 + fi + done < "$TMP/dashboards.txt" + + [ "$failed" -eq 0 ] +} + +@test "every DCGM metric referenced in tracked dashboards and rules is declared" { + TMP=$(mktemp -d) + trap 'rm -rf "$TMP"' EXIT + + [ -f "$DCGM_DEFAULT_CSV" ] || { echo "missing $DCGM_DEFAULT_CSV" >&2; exit 1; } + [ -f "$DCGM_CUSTOM_CSV" ] || { echo "missing $DCGM_CUSTOM_CSV" >&2; exit 1; } + + { + extract_csv_metrics "$DCGM_DEFAULT_CSV" + extract_csv_metrics "$DCGM_CUSTOM_CSV" + } | sort -u > "$TMP/declared.txt" + [ -s "$TMP/declared.txt" ] || { echo "no DCGM metrics extracted from CSVs" >&2; exit 1; } + + list_tracked_gpu_dashboards > "$TMP/dashboards.txt" + [ -s "$TMP/dashboards.txt" ] || { echo "no gpu/* dashboards listed in $DASHBOARDS_LIST" >&2; exit 1; } + + failed=0 + + # Dashboard coverage — every dashboard's DCGM references must resolve. + while IFS= read -r dashboard_rel; do + dashboard="$DASHBOARDS_DIR/$dashboard_rel" + [ -f "$dashboard" ] || continue # handled by the existence test + extract_dcgm_refs "$dashboard" > "$TMP/refs.txt" + [ -s "$TMP/refs.txt" ] || continue # dashboard relies entirely on recording rules + comm -23 "$TMP/refs.txt" "$TMP/declared.txt" > "$TMP/missing.txt" + if [ -s "$TMP/missing.txt" ]; then + echo "ERROR: $dashboard_rel references DCGM metrics not declared in any CSV:" >&2 + sed 's/^/ - /' "$TMP/missing.txt" >&2 + failed=1 + fi + done < "$TMP/dashboards.txt" + + # Rules coverage — recording rules consume DCGM directly, so their set + # must be declared too, otherwise derived series on every dashboard + # collapse to empty. + extract_dcgm_refs "$RULES_FILE" > "$TMP/rule-refs.txt" + if [ -s "$TMP/rule-refs.txt" ]; then + comm -23 "$TMP/rule-refs.txt" "$TMP/declared.txt" > "$TMP/rule-missing.txt" + if [ -s "$TMP/rule-missing.txt" ]; then + echo "ERROR: gpu-recording.rules.yaml references DCGM metrics not declared in any CSV:" >&2 + sed 's/^/ - /' "$TMP/rule-missing.txt" >&2 + failed=1 + fi + fi + + [ "$failed" -eq 0 ] +} + +@test "every tracked GPU dashboard listed in dashboards-infra.list exists on disk" { + TMP=$(mktemp -d) + trap 'rm -rf "$TMP"' EXIT + + list_tracked_gpu_dashboards > "$TMP/dashboards.txt" + [ -s "$TMP/dashboards.txt" ] || { echo "no gpu/* dashboards listed in $DASHBOARDS_LIST" >&2; exit 1; } + + failed=0 + while IFS= read -r dashboard_rel; do + dashboard="$DASHBOARDS_DIR/$dashboard_rel" + if [ ! -f "$dashboard" ]; then + echo "ERROR: $dashboard_rel listed in dashboards-infra.list but $dashboard does not exist" >&2 + failed=1 + fi + done < "$TMP/dashboards.txt" + + [ "$failed" -eq 0 ] +} diff --git a/hack/check-host-runtime.bats b/hack/check-host-runtime.bats new file mode 100644 index 00000000..b121b31b --- /dev/null +++ b/hack/check-host-runtime.bats @@ -0,0 +1,415 @@ +#!/usr/bin/env bats +# ----------------------------------------------------------------------------- +# Unit tests for hack/check-host-runtime.sh +# +# The script warns when a standalone containerd.service or docker.service is +# active alongside the embedded k3s runtime on Ubuntu hosts running the +# cozystack "generic" variant. Warnings go to stderr; exit code is always 0. +# +# Test strategy: each test builds its own temporary stub directory and prepends +# it to PATH to inject a fake `systemctl` (and optionally `du`) binary. The +# script itself honors a small set of COZYSTACK_PREFLIGHT_* environment +# variables to redirect socket/dir probes into the stub tree, so tests do not +# need root privileges or a real systemd host. +# +# Each test installs a `trap 'rm -rf "$STUB_DIR"' EXIT` immediately after +# creating the stub dir so cleanup runs even when an assertion fails mid-test +# under `set -e`. cozytest.sh runs each @test in its own subshell, so traps +# scope per test and do not leak across tests. +# +# Tests are otherwise self-contained — no shared setup/teardown helpers, +# because cozytest.sh's awk parser only recognizes @test blocks and treats a +# bare `}` on its own line as the end of a test function. +# +# Title syntax constraints (inherited from cozytest.sh's awk parser): +# - Titles must be delimited by ASCII double quotes; embedded literal +# double quotes are NOT escaped and will silently truncate the title. +# - Only alphanumeric characters from the title survive into the shell +# function name (everything else becomes '_'), so titles that differ +# only in punctuation collapse to the same function name. Keep titles +# distinctive in their alphanumeric run. +# +# Run with: hack/cozytest.sh hack/check-host-runtime.bats +# (or `bats hack/check-host-runtime.bats` if the bats binary is +# installed; cozytest.sh is the CI path.) +# ----------------------------------------------------------------------------- + +@test "clean host with no runtime services exits silently" { + STUB_DIR=$(mktemp -d) + trap 'rm -rf "$STUB_DIR"' EXIT + + cat >"$STUB_DIR/systemctl" <<'STUBEOF' +#!/bin/sh +if [ "$1" = "--version" ]; then + echo "systemd stub" + exit 0 +fi +exit 1 +STUBEOF + chmod +x "$STUB_DIR/systemctl" + + STDERR_FILE="$STUB_DIR/stderr" + COZYSTACK_CONTAINERD_SOCKET="$STUB_DIR/missing-containerd.sock" \ + COZYSTACK_DOCKER_SOCKET_PATHS="$STUB_DIR/missing-docker1.sock $STUB_DIR/missing-docker2.sock" \ + COZYSTACK_CONTAINERD_DIR="$STUB_DIR/missing-containerd-dir" \ + COZYSTACK_DOCKER_DIR="$STUB_DIR/missing-docker-dir" \ + PATH="$STUB_DIR:$PATH" \ + bash hack/check-host-runtime.sh >"$STUB_DIR/stdout" 2>"$STDERR_FILE" + + [ ! -s "$STDERR_FILE" ] + [ ! -s "$STUB_DIR/stdout" ] +} + +@test "standalone containerd service active prints warning" { + STUB_DIR=$(mktemp -d) + trap 'rm -rf "$STUB_DIR"' EXIT + + cat >"$STUB_DIR/systemctl" <<'STUBEOF' +#!/bin/sh +if [ "$1" = "--version" ]; then + echo "systemd stub" + exit 0 +fi +if [ "$1" = "is-active" ] && [ "$2" = "containerd.service" ]; then + echo active + exit 0 +fi +exit 1 +STUBEOF + chmod +x "$STUB_DIR/systemctl" + + mkdir -p "$STUB_DIR/var-lib-containerd" + echo dummy >"$STUB_DIR/var-lib-containerd/dummy" + + STDERR_FILE="$STUB_DIR/stderr" + COZYSTACK_CONTAINERD_SOCKET="$STUB_DIR/missing-containerd.sock" \ + COZYSTACK_DOCKER_SOCKET_PATHS="$STUB_DIR/missing-docker.sock" \ + COZYSTACK_CONTAINERD_DIR="$STUB_DIR/var-lib-containerd" \ + COZYSTACK_DOCKER_DIR="$STUB_DIR/missing-docker-dir" \ + PATH="$STUB_DIR:$PATH" \ + bash hack/check-host-runtime.sh 2>"$STDERR_FILE" + + grep -q 'standalone containerd.service' "$STDERR_FILE" + if grep -q 'standalone docker.service' "$STDERR_FILE"; then + echo "unexpected docker warning found:" >&2 + cat "$STDERR_FILE" >&2 + exit 1 + fi + # HINT line must name only the detected service, not advise disabling + # docker.service when only containerd.service is running. The sudo + # prefix is also required — without it the command silently no-ops + # for a non-root operator, so the prefix is part of the contract. + grep -q 'sudo systemctl disable --now containerd.service' "$STDERR_FILE" + if grep -q 'systemctl disable --now.*docker' "$STDERR_FILE"; then + echo "HINT unexpectedly mentions docker:" >&2 + cat "$STDERR_FILE" >&2 + exit 1 + fi +} + +@test "standalone docker service active prints warning" { + STUB_DIR=$(mktemp -d) + trap 'rm -rf "$STUB_DIR"' EXIT + + cat >"$STUB_DIR/systemctl" <<'STUBEOF' +#!/bin/sh +if [ "$1" = "--version" ]; then + echo "systemd stub" + exit 0 +fi +if [ "$1" = "is-active" ] && [ "$2" = "docker.service" ]; then + echo active + exit 0 +fi +exit 1 +STUBEOF + chmod +x "$STUB_DIR/systemctl" + + mkdir -p "$STUB_DIR/var-lib-docker" + echo dummy >"$STUB_DIR/var-lib-docker/dummy" + + STDERR_FILE="$STUB_DIR/stderr" + COZYSTACK_CONTAINERD_SOCKET="$STUB_DIR/missing-containerd.sock" \ + COZYSTACK_DOCKER_SOCKET_PATHS="$STUB_DIR/missing-docker.sock" \ + COZYSTACK_CONTAINERD_DIR="$STUB_DIR/missing-containerd-dir" \ + COZYSTACK_DOCKER_DIR="$STUB_DIR/var-lib-docker" \ + PATH="$STUB_DIR:$PATH" \ + bash hack/check-host-runtime.sh 2>"$STDERR_FILE" + + grep -q 'standalone docker.service' "$STDERR_FILE" + if grep -q 'standalone containerd.service' "$STDERR_FILE"; then + echo "unexpected containerd warning found:" >&2 + cat "$STDERR_FILE" >&2 + exit 1 + fi + # HINT line must name only the detected service. As in the + # containerd test, the sudo prefix is part of the contract. + grep -q 'sudo systemctl disable --now docker.service' "$STDERR_FILE" + if grep -q 'systemctl disable --now.*containerd' "$STDERR_FILE"; then + echo "HINT unexpectedly mentions containerd:" >&2 + cat "$STDERR_FILE" >&2 + exit 1 + fi +} + +@test "both services active prints two warnings and the HINT block" { + STUB_DIR=$(mktemp -d) + trap 'rm -rf "$STUB_DIR"' EXIT + + cat >"$STUB_DIR/systemctl" <<'STUBEOF' +#!/bin/sh +if [ "$1" = "--version" ]; then + echo "systemd stub" + exit 0 +fi +if [ "$1" = "is-active" ]; then + case "$2" in + containerd.service|docker.service) echo active; exit 0 ;; + esac +fi +exit 1 +STUBEOF + chmod +x "$STUB_DIR/systemctl" + + mkdir -p "$STUB_DIR/var-lib-containerd" "$STUB_DIR/var-lib-docker" + + STDERR_FILE="$STUB_DIR/stderr" + # Capture exit code explicitly: the script contract says exit 0 + # unconditionally (warning, not blocker). `set -e` in the test + # function body would already fail on a nonzero exit, but an + # explicit status check locks in the contract and makes a + # regression show up as "expected 0, got N" rather than as a + # generic test failure. + status=0 + COZYSTACK_CONTAINERD_SOCKET="$STUB_DIR/missing-containerd.sock" \ + COZYSTACK_DOCKER_SOCKET_PATHS="$STUB_DIR/missing-docker.sock" \ + COZYSTACK_CONTAINERD_DIR="$STUB_DIR/var-lib-containerd" \ + COZYSTACK_DOCKER_DIR="$STUB_DIR/var-lib-docker" \ + PATH="$STUB_DIR:$PATH" \ + bash hack/check-host-runtime.sh 2>"$STDERR_FILE" || status=$? + + [ "$status" -eq 0 ] + grep -q 'standalone containerd.service' "$STDERR_FILE" + grep -q 'standalone docker.service' "$STDERR_FILE" + # HINT block must fire whenever warnings exist; otherwise a future silent + # removal of the HINT would go unnoticed. When both services fire the HINT + # must list both in a single sudo systemctl disable invocation — the sudo + # prefix is as important as the systemctl verb, otherwise the operator + # would be told to run it as a non-root user and quietly fail. + grep -q 'HINT:' "$STDERR_FILE" + grep -q 'sudo systemctl disable --now containerd.service docker.service' "$STDERR_FILE" +} + +@test "failing du does not suppress the containerd warning" { + STUB_DIR=$(mktemp -d) + trap 'rm -rf "$STUB_DIR"' EXIT + + cat >"$STUB_DIR/systemctl" <<'STUBEOF' +#!/bin/sh +if [ "$1" = "--version" ]; then + echo "systemd stub" + exit 0 +fi +if [ "$1" = "is-active" ] && [ "$2" = "containerd.service" ]; then + echo active + exit 0 +fi +exit 1 +STUBEOF + chmod +x "$STUB_DIR/systemctl" + cat >"$STUB_DIR/du" <<'DUEOF' +#!/bin/sh +exit 1 +DUEOF + chmod +x "$STUB_DIR/du" + + mkdir -p "$STUB_DIR/var-lib-containerd" + + STDERR_FILE="$STUB_DIR/stderr" + COZYSTACK_CONTAINERD_SOCKET="$STUB_DIR/missing-containerd.sock" \ + COZYSTACK_DOCKER_SOCKET_PATHS="$STUB_DIR/missing-docker.sock" \ + COZYSTACK_CONTAINERD_DIR="$STUB_DIR/var-lib-containerd" \ + COZYSTACK_DOCKER_DIR="$STUB_DIR/missing-docker-dir" \ + PATH="$STUB_DIR:$PATH" \ + bash hack/check-host-runtime.sh 2>"$STDERR_FILE" + + grep -q 'standalone containerd.service' "$STDERR_FILE" +} + +@test "containerd socket fallback fires when systemctl is unavailable" { + STUB_DIR=$(mktemp -d) + trap 'rm -rf "$STUB_DIR"' EXIT + + # The script uses `[ -e "$sock" ]`, not `[ -S ... ]`, so a regular + # file is a valid stand-in for a unix socket in tests. This also + # removes any optional runtime dependency on python3 and makes the + # test unconditional on every CI runner. + SOCK="$STUB_DIR/containerd.sock" + touch "$SOCK" + + STDERR_FILE="$STUB_DIR/stderr" + COZYSTACK_PREFLIGHT_FORCE_NO_SYSTEMCTL=1 \ + COZYSTACK_CONTAINERD_SOCKET="$SOCK" \ + COZYSTACK_DOCKER_SOCKET_PATHS="$STUB_DIR/missing-docker.sock" \ + COZYSTACK_CONTAINERD_DIR="$STUB_DIR/missing-containerd-dir" \ + COZYSTACK_DOCKER_DIR="$STUB_DIR/missing-docker-dir" \ + bash hack/check-host-runtime.sh 2>"$STDERR_FILE" + + grep -q 'standalone containerd.service' "$STDERR_FILE" +} + +@test "docker socket fallback fires when systemctl is unavailable" { + STUB_DIR=$(mktemp -d) + trap 'rm -rf "$STUB_DIR"' EXIT + + SOCK="$STUB_DIR/docker.sock" + touch "$SOCK" + + STDERR_FILE="$STUB_DIR/stderr" + COZYSTACK_PREFLIGHT_FORCE_NO_SYSTEMCTL=1 \ + COZYSTACK_CONTAINERD_SOCKET="$STUB_DIR/missing-containerd.sock" \ + COZYSTACK_DOCKER_SOCKET_PATHS="$SOCK" \ + COZYSTACK_CONTAINERD_DIR="$STUB_DIR/missing-containerd-dir" \ + COZYSTACK_DOCKER_DIR="$STUB_DIR/missing-docker-dir" \ + bash hack/check-host-runtime.sh 2>"$STDERR_FILE" + + grep -q 'standalone docker.service' "$STDERR_FILE" + if grep -q 'standalone containerd.service' "$STDERR_FILE"; then + echo "unexpected containerd warning found:" >&2 + cat "$STDERR_FILE" >&2 + exit 1 + fi +} + +@test "clean host without systemctl exits silently" { + STUB_DIR=$(mktemp -d) + trap 'rm -rf "$STUB_DIR"' EXIT + + STDERR_FILE="$STUB_DIR/stderr" + COZYSTACK_PREFLIGHT_FORCE_NO_SYSTEMCTL=1 \ + COZYSTACK_CONTAINERD_SOCKET="$STUB_DIR/missing-containerd.sock" \ + COZYSTACK_DOCKER_SOCKET_PATHS="$STUB_DIR/missing-docker1.sock $STUB_DIR/missing-docker2.sock" \ + COZYSTACK_CONTAINERD_DIR="$STUB_DIR/missing-containerd-dir" \ + COZYSTACK_DOCKER_DIR="$STUB_DIR/missing-docker-dir" \ + bash hack/check-host-runtime.sh >"$STUB_DIR/stdout" 2>"$STDERR_FILE" + + [ ! -s "$STDERR_FILE" ] + [ ! -s "$STUB_DIR/stdout" ] +} + +@test "docker service plus socket still emits exactly one warning" { + STUB_DIR=$(mktemp -d) + trap 'rm -rf "$STUB_DIR"' EXIT + + cat >"$STUB_DIR/systemctl" <<'STUBEOF' +#!/bin/sh +if [ "$1" = "--version" ]; then + echo "systemd stub" + exit 0 +fi +if [ "$1" = "is-active" ] && [ "$2" = "docker.service" ]; then + echo active + exit 0 +fi +exit 1 +STUBEOF + chmod +x "$STUB_DIR/systemctl" + + SOCK="$STUB_DIR/docker.sock" + touch "$SOCK" + + mkdir -p "$STUB_DIR/var-lib-docker" + + STDERR_FILE="$STUB_DIR/stderr" + COZYSTACK_CONTAINERD_SOCKET="$STUB_DIR/missing-containerd.sock" \ + COZYSTACK_DOCKER_SOCKET_PATHS="$SOCK" \ + COZYSTACK_CONTAINERD_DIR="$STUB_DIR/missing-containerd-dir" \ + COZYSTACK_DOCKER_DIR="$STUB_DIR/var-lib-docker" \ + PATH="$STUB_DIR:$PATH" \ + bash hack/check-host-runtime.sh 2>"$STDERR_FILE" + + count=$(grep -c 'standalone docker.service' "$STDERR_FILE") + if [ "$count" != "1" ]; then + echo "expected exactly one docker warning, got $count" >&2 + cat "$STDERR_FILE" >&2 + exit 1 + fi +} + +@test "docker socket paths with glob chars do not expand" { + STUB_DIR=$(mktemp -d) + trap 'rm -rf "$STUB_DIR"' EXIT + + # Create two directories that a naive `for sock in $PATHS` loop + # would glob-expand and treat as existing "sockets". With the + # array-based parsing the literal path "$STUB_DIR/var-lib-*" does + # not exist and no warning must fire. + mkdir -p "$STUB_DIR/var-lib-docker" "$STUB_DIR/var-lib-containerd" + + cat >"$STUB_DIR/systemctl" <<'STUBEOF' +#!/bin/sh +if [ "$1" = "--version" ]; then + echo "systemd stub" + exit 0 +fi +exit 1 +STUBEOF + chmod +x "$STUB_DIR/systemctl" + + STDERR_FILE="$STUB_DIR/stderr" + COZYSTACK_CONTAINERD_SOCKET="$STUB_DIR/missing-containerd.sock" \ + COZYSTACK_DOCKER_SOCKET_PATHS="$STUB_DIR/var-lib-*" \ + COZYSTACK_CONTAINERD_DIR="$STUB_DIR/missing-containerd-dir" \ + COZYSTACK_DOCKER_DIR="$STUB_DIR/missing-docker-dir" \ + PATH="$STUB_DIR:$PATH" \ + bash hack/check-host-runtime.sh 2>"$STDERR_FILE" + + if grep -q 'standalone docker.service' "$STDERR_FILE"; then + echo "glob pattern expanded — docker warning should not fire:" >&2 + cat "$STDERR_FILE" >&2 + exit 1 + fi +} + +@test "containerd service plus socket still emits exactly one warning" { + STUB_DIR=$(mktemp -d) + trap 'rm -rf "$STUB_DIR"' EXIT + + cat >"$STUB_DIR/systemctl" <<'STUBEOF' +#!/bin/sh +if [ "$1" = "--version" ]; then + echo "systemd stub" + exit 0 +fi +if [ "$1" = "is-active" ] && [ "$2" = "containerd.service" ]; then + echo active + exit 0 +fi +exit 1 +STUBEOF + chmod +x "$STUB_DIR/systemctl" + + # The script uses `[ -e "$sock" ]`, not `[ -S ... ]`, so a regular + # file is a valid stand-in for a unix socket in tests. This also + # removes any optional runtime dependency on python3 and makes the + # test unconditional on every CI runner. + SOCK="$STUB_DIR/containerd.sock" + touch "$SOCK" + + mkdir -p "$STUB_DIR/var-lib-containerd" + + STDERR_FILE="$STUB_DIR/stderr" + COZYSTACK_CONTAINERD_SOCKET="$SOCK" \ + COZYSTACK_DOCKER_SOCKET_PATHS="$STUB_DIR/missing-docker.sock" \ + COZYSTACK_CONTAINERD_DIR="$STUB_DIR/var-lib-containerd" \ + COZYSTACK_DOCKER_DIR="$STUB_DIR/missing-docker-dir" \ + PATH="$STUB_DIR:$PATH" \ + bash hack/check-host-runtime.sh 2>"$STDERR_FILE" + + count=$(grep -c 'standalone containerd.service' "$STDERR_FILE") + if [ "$count" != "1" ]; then + echo "expected exactly one containerd warning, got $count" >&2 + cat "$STDERR_FILE" >&2 + exit 1 + fi +} diff --git a/hack/check-host-runtime.sh b/hack/check-host-runtime.sh new file mode 100755 index 00000000..917453d2 --- /dev/null +++ b/hack/check-host-runtime.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env bash +# ----------------------------------------------------------------------------- +# check-host-runtime.sh — operator preflight warning +# +# Purpose: +# Warn when a standalone containerd.service or docker.service is running on +# the host alongside the embedded k3s runtime. This mismatch is silent on +# day 0 (k3s uses its own containerd at /run/k3s/containerd/containerd.sock +# and /var/lib/rancher/k3s/agent/containerd) but over time the standalone +# runtime accumulates unpruned images and build cache in /var/lib/containerd +# — enough to trigger DiskPressure and crash cozystack-api with eviction +# loops. The script does NOT block install; it only prints a warning. +# +# When to run: +# Before `helm install cozy-installer` on an Ubuntu host prepared with k3s +# or kubeadm (cozystack "generic" variant). Irrelevant on Talos where the +# container runtime lifecycle is fully managed. Discoverable via +# `make preflight` from the repository root. +# +# Exit code: +# Always 0 (warning, not a blocker). Warnings go to stderr. +# +# Environment variables (test hooks — override default probe paths): +# COZYSTACK_CONTAINERD_SOCKET standalone containerd socket path +# COZYSTACK_DOCKER_SOCKET_PATHS space-separated list of docker socket paths +# COZYSTACK_CONTAINERD_DIR standalone containerd data directory +# COZYSTACK_DOCKER_DIR standalone docker data directory +# COZYSTACK_PREFLIGHT_FORCE_NO_SYSTEMCTL=1 pretend systemctl is absent +# ----------------------------------------------------------------------------- +set -euo pipefail + +if [ -t 2 ]; then + YELLOW=$'\033[1;33m' + RESET=$'\033[0m' +else + YELLOW='' + RESET='' +fi + +CONTAINERD_SOCKET=${COZYSTACK_CONTAINERD_SOCKET:-/run/containerd/containerd.sock} +DOCKER_SOCKET_PATHS=${COZYSTACK_DOCKER_SOCKET_PATHS:-/run/docker.sock /var/run/docker.sock} +CONTAINERD_DIR=${COZYSTACK_CONTAINERD_DIR:-/var/lib/containerd} +DOCKER_DIR=${COZYSTACK_DOCKER_DIR:-/var/lib/docker} + +CONTAINERD_WARN=0 +DOCKER_WARN=0 + +warn() { + printf '%sWARNING:%s %s\n' "$YELLOW" "$RESET" "$1" >&2 +} + +detect_systemctl() { + if [ "${COZYSTACK_PREFLIGHT_FORCE_NO_SYSTEMCTL:-0}" = "1" ]; then + return 1 + fi + if command -v systemctl >/dev/null 2>&1 && systemctl --version >/dev/null 2>&1; then + return 0 + fi + return 1 +} + +disk_usage() { + local path=$1 + local usage + if [ -d "$path" ]; then + # Wrap `du` in `timeout 5s` so a container data directory with + # millions of files (the exact scenario this script exists to + # warn about) cannot stall the preflight indefinitely. If the + # `timeout` binary is absent the pipeline still exits 0 via + # `|| true` and `usage` stays empty; the warning itself is + # still printed, just without the size detail. + usage=$(timeout 5s du -sh "$path" 2>/dev/null | awk '{print $1}' || true) + if [ -n "${usage:-}" ]; then + printf ' (%s uses %s)' "$path" "$usage" + fi + fi +} + +service_active() { + local service=$1 + if [ "$HAS_SYSTEMCTL" = "1" ]; then + if systemctl is-active "$service" >/dev/null 2>&1; then + return 0 + fi + fi + return 1 +} + +check_containerd() { + local detail="" + local found=0 + if service_active containerd.service; then + found=1 + fi + if [ "$found" -eq 0 ] && [ -e "$CONTAINERD_SOCKET" ]; then + found=1 + fi + if [ "$found" -eq 1 ]; then + detail=$(disk_usage "$CONTAINERD_DIR") + warn "standalone containerd.service detected alongside k3s embedded runtime${detail}" + CONTAINERD_WARN=1 + fi +} + +check_docker() { + local detail="" + local found=0 + if service_active docker.service; then + found=1 + fi + if [ "$found" -eq 0 ]; then + # DOCKER_SOCKET_PATHS is a space separated list of paths. Parse + # it into an array via `read -ra` so that word splitting is + # explicit AND glob expansion is suppressed — `for sock in + # $DOCKER_SOCKET_PATHS` would both word split and glob, so a + # path containing a literal `*` or `?` could expand into + # directory entries and produce false positives. + local -a _socks + read -ra _socks <<<"$DOCKER_SOCKET_PATHS" + for sock in "${_socks[@]}"; do + if [ -e "$sock" ]; then + found=1 + break + fi + done + fi + if [ "$found" -eq 1 ]; then + detail=$(disk_usage "$DOCKER_DIR") + warn "standalone docker.service detected alongside k3s embedded runtime${detail}" + DOCKER_WARN=1 + fi +} + +if detect_systemctl; then + HAS_SYSTEMCTL=1 +else + HAS_SYSTEMCTL=0 +fi + +check_containerd +check_docker + +if [ "$CONTAINERD_WARN" -eq 1 ] || [ "$DOCKER_WARN" -eq 1 ]; then + services="" + if [ "$CONTAINERD_WARN" -eq 1 ]; then + services="containerd.service" + fi + if [ "$DOCKER_WARN" -eq 1 ]; then + if [ -n "$services" ]; then + services="$services docker.service" + else + services="docker.service" + fi + fi + printf '%sHINT:%s cozystack runs its own containerd under k3s. To stop the shadow runtime:\n' "$YELLOW" "$RESET" >&2 + printf ' sudo systemctl disable --now %s\n' "$services" >&2 + printf 'Inspect and reclaim standalone runtime storage separately — it may contain container data\n' >&2 + printf 'that the operator still needs; do not delete it blindly.\n' >&2 +fi + +exit 0 diff --git a/hack/check-readiness.sh b/hack/check-readiness.sh new file mode 100755 index 00000000..bd3a3ffc --- /dev/null +++ b/hack/check-readiness.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +# Check readiness of Packages, ArtifactGenerators, ExternalArtifacts, and HelmReleases +# Shows only non-ready resources +# +# Usage: check-readiness.sh [-w [INTERVAL]] +# -w [INTERVAL] Watch mode: refresh continuously every INTERVAL seconds (default: 5) + +set -euo pipefail + +KUBECTL="kubectl" +if [[ -n "${KUBECONFIG:-}" ]]; then + KUBECTL="kubectl --kubeconfig=${KUBECONFIG}" +fi + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BOLD='\033[1m' +RESET='\033[0m' + +WATCH=0 +INTERVAL=5 + +while [[ $# -gt 0 ]]; do + case "$1" in + -w|--watch) + WATCH=1 + if [[ $# -gt 1 && "$2" =~ ^[0-9]+$ ]]; then + INTERVAL="$2" + shift + fi + shift + ;; + *) echo "Unknown argument: $1"; exit 2 ;; + esac +done + +check_resource() { + local kind="$1" + local header_printed=0 + + while IFS= read -r line; do + if [[ "$line" =~ ^(NAME|NAMESPACE) ]]; then + continue + fi + if ! echo "$line" | awk '{for(i=1;i<=NF;i++) if($i=="True") exit 0; exit 1}'; then + if [[ $header_printed -eq 0 ]]; then + echo -e "${BOLD}${YELLOW}=== ${kind} (not ready) ===${RESET}" + header_printed=1 + fi + echo -e "${RED}${line}${RESET}" + found_issues=1 + fi + done < <($KUBECTL get "$kind" -A 2>/dev/null) + + return 0 +} + +run_once() { + found_issues=0 + + check_resource packages.cozystack.io + check_resource artifactgenerators.source.extensions.fluxcd.io + check_resource externalartifacts.source.toolkit.fluxcd.io + check_resource helmreleases.helm.toolkit.fluxcd.io + + if [[ $found_issues -eq 0 ]]; then + echo -e "${GREEN}${BOLD}All resources are ready.${RESET}" + fi +} + +if [[ $WATCH -eq 1 ]]; then + while true; do + output=$(run_once 2>&1) + clear + echo -e "${BOLD}Last updated: $(date) (refreshing every ${INTERVAL}s, Ctrl+C to stop)${RESET}\n" + echo -e "$output" + sleep "$INTERVAL" + done +else + run_once + [[ $found_issues -eq 0 ]] || exit 1 +fi diff --git a/hack/common-envs.mk b/hack/common-envs.mk index 56eb5478..9b8db5db 100644 --- a/hack/common-envs.mk +++ b/hack/common-envs.mk @@ -6,13 +6,13 @@ else endif REGISTRY ?= ghcr.io/cozystack/cozystack -TAG = $(shell git describe --tags --exact-match 2>/dev/null || echo latest) +TAG = $(shell git describe --tags --exact-match --match 'v*' 2>/dev/null || echo latest) PUSH := 1 LOAD := 0 BUILDER ?= PLATFORM ?= BUILDX_EXTRA_ARGS ?= -COZYSTACK_VERSION = $(patsubst v%,%,$(shell git describe --tags)) +COZYSTACK_VERSION = $(patsubst v%,%,$(shell git describe --tags --match 'v*')) BUILDX_ARGS := --provenance=false --push=$(PUSH) --load=$(LOAD) \ --label org.opencontainers.image.source=https://github.com/cozystack/cozystack \ @@ -28,6 +28,6 @@ endef ifeq ($(COZYSTACK_VERSION),) $(shell git remote add upstream https://github.com/cozystack/cozystack.git || true) $(shell git fetch upstream --tags) - COZYSTACK_VERSION = $(patsubst v%,%,$(shell git describe --tags)) + COZYSTACK_VERSION = $(patsubst v%,%,$(shell git describe --tags --match 'v*')) endif diff --git a/hack/cozytest.sh b/hack/cozytest.sh index ff476e08..a3955fd1 100755 --- a/hack/cozytest.sh +++ b/hack/cozytest.sh @@ -10,7 +10,11 @@ PATTERN=${2:-*} LINE='----------------------------------------------------------------' cols() { stty size 2>/dev/null | awk '{print $2}' || echo 80; } -MAXW=$(( $(cols) - 12 )); [ "$MAXW" -lt 40 ] && MAXW=70 +if [ -t 1 ]; then + MAXW=$(( $(cols) - 12 )); [ "$MAXW" -lt 40 ] && MAXW=70 +else + MAXW=0 # no truncation when not a tty (e.g. CI) +fi BEGIN=$(date +%s) timestamp() { s=$(( $(date +%s) - BEGIN )); printf '[%02d:%02d]' $((s/60)) $((s%60)); } @@ -45,7 +49,7 @@ run_one() { *) out=$line ;; esac now=$(( $(date +%s) - START )) - [ ${#out} -gt "$MAXW" ] && out="$(printf '%.*s…' "$MAXW" "$out")" + [ "$MAXW" -gt 0 ] && [ ${#out} -gt "$MAXW" ] && out="$(printf '%.*s…' "$MAXW" "$out")" printf '┊[%02d:%02d] %s\n' $((now/60)) $((now%60)) "$out" done @@ -61,6 +65,7 @@ run_one() { echo "----- captured output -----------------------------------------" grep -v '^__RC__' "$log" echo "$LINE" + rm -rf "$tmp" exit "$rc" fi diff --git a/hack/dcgm-default-counters.csv b/hack/dcgm-default-counters.csv new file mode 100644 index 00000000..b5e94540 --- /dev/null +++ b/hack/dcgm-default-counters.csv @@ -0,0 +1,104 @@ +# Snapshot of the upstream DCGM Exporter default-counters.csv used as a +# fixture by hack/check-gpu-recording-rules.bats. The test verifies that +# every DCGM_FI_* metric referenced by a tracked GPU dashboard is either +# declared here (upstream defaults) or in +# packages/system/gpu-operator/examples/dcgm-custom-metrics.yaml +# (the project's custom CSV). +# +# Source: https://github.com/NVIDIA/dcgm-exporter/blob/4.1.1-4.0.4/etc/default-counters.csv +# Pinned to the DCGM Exporter image tag shipped by the gpu-operator +# chart under packages/system/gpu-operator/charts/gpu-operator/values.yaml +# (dcgmExporter.version = 4.1.1-4.0.4-ubuntu22.04). When that image is +# bumped, refresh this file from the matching tag in the NVIDIA/dcgm-exporter +# repo and re-run `./hack/cozytest.sh hack/check-gpu-recording-rules.bats`. + +# Format +# If line starts with a '#' it is considered a comment +# DCGM FIELD, Prometheus metric type, help message + +# Clocks +DCGM_FI_DEV_SM_CLOCK, gauge, SM clock frequency (in MHz). +DCGM_FI_DEV_MEM_CLOCK, gauge, Memory clock frequency (in MHz). + +# Temperature +DCGM_FI_DEV_MEMORY_TEMP, gauge, Memory temperature (in C). +DCGM_FI_DEV_GPU_TEMP, gauge, GPU temperature (in C). + +# Power +DCGM_FI_DEV_POWER_USAGE, gauge, Power draw (in W). +DCGM_FI_DEV_TOTAL_ENERGY_CONSUMPTION, counter, Total energy consumption since boot (in mJ). + +# PCIE +# DCGM_FI_PROF_PCIE_TX_BYTES, counter, Total number of bytes transmitted through PCIe TX via NVML. +# DCGM_FI_PROF_PCIE_RX_BYTES, counter, Total number of bytes received through PCIe RX via NVML. +DCGM_FI_DEV_PCIE_REPLAY_COUNTER, counter, Total number of PCIe retries. + +# Utilization (the sample period varies depending on the product) +DCGM_FI_DEV_GPU_UTIL, gauge, GPU utilization (in %). +DCGM_FI_DEV_MEM_COPY_UTIL, gauge, Memory utilization (in %). +DCGM_FI_DEV_ENC_UTIL, gauge, Encoder utilization (in %). +DCGM_FI_DEV_DEC_UTIL , gauge, Decoder utilization (in %). + +# Errors and violations +DCGM_FI_DEV_XID_ERRORS, gauge, Value of the last XID error encountered. +# DCGM_FI_DEV_POWER_VIOLATION, counter, Throttling duration due to power constraints (in us). +# DCGM_FI_DEV_THERMAL_VIOLATION, counter, Throttling duration due to thermal constraints (in us). +# DCGM_FI_DEV_SYNC_BOOST_VIOLATION, counter, Throttling duration due to sync-boost constraints (in us). +# DCGM_FI_DEV_BOARD_LIMIT_VIOLATION, counter, Throttling duration due to board limit constraints (in us). +# DCGM_FI_DEV_LOW_UTIL_VIOLATION, counter, Throttling duration due to low utilization (in us). +# DCGM_FI_DEV_RELIABILITY_VIOLATION, counter, Throttling duration due to reliability constraints (in us). + +# Memory usage +DCGM_FI_DEV_FB_FREE, gauge, Framebuffer memory free (in MiB). +DCGM_FI_DEV_FB_USED, gauge, Framebuffer memory used (in MiB). + +# ECC +# DCGM_FI_DEV_ECC_SBE_VOL_TOTAL, counter, Total number of single-bit volatile ECC errors. +# DCGM_FI_DEV_ECC_DBE_VOL_TOTAL, counter, Total number of double-bit volatile ECC errors. +# DCGM_FI_DEV_ECC_SBE_AGG_TOTAL, counter, Total number of single-bit persistent ECC errors. +# DCGM_FI_DEV_ECC_DBE_AGG_TOTAL, counter, Total number of double-bit persistent ECC errors. + +# Retired pages +# DCGM_FI_DEV_RETIRED_SBE, counter, Total number of retired pages due to single-bit errors. +# DCGM_FI_DEV_RETIRED_DBE, counter, Total number of retired pages due to double-bit errors. +# DCGM_FI_DEV_RETIRED_PENDING, counter, Total number of pages pending retirement. + +# NVLink +# DCGM_FI_DEV_NVLINK_CRC_FLIT_ERROR_COUNT_TOTAL, counter, Total number of NVLink flow-control CRC errors. +# DCGM_FI_DEV_NVLINK_CRC_DATA_ERROR_COUNT_TOTAL, counter, Total number of NVLink data CRC errors. +# DCGM_FI_DEV_NVLINK_REPLAY_ERROR_COUNT_TOTAL, counter, Total number of NVLink retries. +# DCGM_FI_DEV_NVLINK_RECOVERY_ERROR_COUNT_TOTAL, counter, Total number of NVLink recovery errors. +DCGM_FI_DEV_NVLINK_BANDWIDTH_TOTAL, counter, Total number of NVLink bandwidth counters for all lanes. +# DCGM_FI_DEV_NVLINK_BANDWIDTH_L0, counter, The number of bytes of active NVLink rx or tx data including both header and payload. + +# VGPU License status +DCGM_FI_DEV_VGPU_LICENSE_STATUS, gauge, vGPU License status + +# Remapped rows +DCGM_FI_DEV_UNCORRECTABLE_REMAPPED_ROWS, counter, Number of remapped rows for uncorrectable errors +DCGM_FI_DEV_CORRECTABLE_REMAPPED_ROWS, counter, Number of remapped rows for correctable errors +DCGM_FI_DEV_ROW_REMAP_FAILURE, gauge, Whether remapping of rows has failed + +# Static configuration information. These appear as labels on the other metrics +DCGM_FI_DRIVER_VERSION, label, Driver Version +# DCGM_FI_NVML_VERSION, label, NVML Version +# DCGM_FI_DEV_BRAND, label, Device Brand +# DCGM_FI_DEV_SERIAL, label, Device Serial Number +# DCGM_FI_DEV_OEM_INFOROM_VER, label, OEM inforom version +# DCGM_FI_DEV_ECC_INFOROM_VER, label, ECC inforom version +# DCGM_FI_DEV_POWER_INFOROM_VER, label, Power management object inforom version +# DCGM_FI_DEV_INFOROM_IMAGE_VER, label, Inforom image version +# DCGM_FI_DEV_VBIOS_VERSION, label, VBIOS version of the device + +# Datacenter Profiling (DCP) metrics +# NOTE: supported on Nvidia datacenter Volta GPUs and newer +DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active. +# DCGM_FI_PROF_SM_ACTIVE, gauge, The ratio of cycles an SM has at least 1 warp assigned. +# DCGM_FI_PROF_SM_OCCUPANCY, gauge, The ratio of number of warps resident on an SM. +DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, Ratio of cycles the tensor (HMMA) pipe is active. +DCGM_FI_PROF_DRAM_ACTIVE, gauge, Ratio of cycles the device memory interface is active sending or receiving data. +# DCGM_FI_PROF_PIPE_FP64_ACTIVE, gauge, Ratio of cycles the fp64 pipes are active. +# DCGM_FI_PROF_PIPE_FP32_ACTIVE, gauge, Ratio of cycles the fp32 pipes are active. +# DCGM_FI_PROF_PIPE_FP16_ACTIVE, gauge, Ratio of cycles the fp16 pipes are active. +DCGM_FI_PROF_PCIE_TX_BYTES, gauge, The rate of data transmitted over the PCIe bus - including both protocol headers and data payloads - in bytes per second. +DCGM_FI_PROF_PCIE_RX_BYTES, gauge, The rate of data received over the PCIe bus - including both protocol headers and data payloads - in bytes per second. diff --git a/hack/e2e-apps/bucket.bats b/hack/e2e-apps/bucket.bats index 2621812a..31e7efb0 100644 --- a/hack/e2e-apps/bucket.bats +++ b/hack/e2e-apps/bucket.bats @@ -1,29 +1,39 @@ #!/usr/bin/env bats @test "Create and Verify Seeweedfs Bucket" { - # Create the bucket resource + # Create the bucket resource with readwrite and readonly users name='test' + kubectl -n tenant-test delete bucket.apps.cozystack.io $name --ignore-not-found --timeout=2m || true + pkill -f "port-forward.*8333:" 2>/dev/null || true kubectl apply -f - < bucket-test-credentials.json + # Get admin (readwrite) credentials + kubectl -n tenant-test get secret bucket-${name}-admin -ojsonpath='{.data.BucketInfo}' | base64 -d > bucket-admin-credentials.json + ADMIN_ACCESS_KEY=$(jq -r '.spec.secretS3.accessKeyID' bucket-admin-credentials.json) + ADMIN_SECRET_KEY=$(jq -r '.spec.secretS3.accessSecretKey' bucket-admin-credentials.json) + BUCKET_NAME=$(jq -r '.spec.bucketName' bucket-admin-credentials.json) - # Get credentials from the secret - ACCESS_KEY=$(jq -r '.spec.secretS3.accessKeyID' bucket-test-credentials.json) - SECRET_KEY=$(jq -r '.spec.secretS3.accessSecretKey' bucket-test-credentials.json) - BUCKET_NAME=$(jq -r '.spec.bucketName' bucket-test-credentials.json) + # Get viewer (readonly) credentials + kubectl -n tenant-test get secret bucket-${name}-viewer -ojsonpath='{.data.BucketInfo}' | base64 -d > bucket-viewer-credentials.json + VIEWER_ACCESS_KEY=$(jq -r '.spec.secretS3.accessKeyID' bucket-viewer-credentials.json) + VIEWER_SECRET_KEY=$(jq -r '.spec.secretS3.accessSecretKey' bucket-viewer-credentials.json) # Start port-forwarding bash -c 'timeout 100s kubectl port-forward service/seaweedfs-s3 -n tenant-root 8333:8333 > /dev/null 2>&1 &' @@ -31,17 +41,33 @@ EOF # Wait for port-forward to be ready timeout 30 sh -ec 'until nc -z localhost 8333; do sleep 1; done' - # Set up MinIO alias with error handling - mc alias set local https://localhost:8333 $ACCESS_KEY $SECRET_KEY --insecure + # --- Test readwrite user (admin) --- + mc alias set rw-user https://localhost:8333 $ADMIN_ACCESS_KEY $ADMIN_SECRET_KEY --insecure - # Upload file to bucket - mc cp bucket-test-credentials.json $BUCKET_NAME/bucket-test-credentials.json + # Admin can upload + echo "readwrite test" > /tmp/rw-test.txt + mc cp --insecure /tmp/rw-test.txt rw-user/$BUCKET_NAME/rw-test.txt - # Verify file was uploaded - mc ls $BUCKET_NAME/bucket-test-credentials.json + # Admin can list + mc ls --insecure rw-user/$BUCKET_NAME/rw-test.txt - # Clean up uploaded file - mc rm $BUCKET_NAME/bucket-test-credentials.json + # Admin can download + mc cp --insecure rw-user/$BUCKET_NAME/rw-test.txt /tmp/rw-test-download.txt + # --- Test readonly user (viewer) --- + mc alias set ro-user https://localhost:8333 $VIEWER_ACCESS_KEY $VIEWER_SECRET_KEY --insecure + + # Viewer can list + mc ls --insecure ro-user/$BUCKET_NAME/rw-test.txt + + # Viewer can download + mc cp --insecure ro-user/$BUCKET_NAME/rw-test.txt /tmp/ro-test-download.txt + + # Viewer cannot upload (must fail with Access Denied) + echo "readonly test" > /tmp/ro-test.txt + ! mc cp --insecure /tmp/ro-test.txt ro-user/$BUCKET_NAME/ro-test.txt + + # --- Cleanup --- + mc rm --insecure rw-user/$BUCKET_NAME/rw-test.txt kubectl -n tenant-test delete bucket.apps.cozystack.io ${name} } diff --git a/hack/e2e-apps/clickhouse.bats b/hack/e2e-apps/clickhouse.bats index 1684c0c4..fbc9f906 100644 --- a/hack/e2e-apps/clickhouse.bats +++ b/hack/e2e-apps/clickhouse.bats @@ -2,6 +2,7 @@ @test "Create DB ClickHouse" { name='test' + kubectl -n tenant-test delete clickhouse.apps.cozystack.io $name --ignore-not-found --timeout=2m || true kubectl apply -f- </dev/null || true +} + +@test "Create and Verify ExternalDNS with inmemory provider" { + name='test' + kubectl apply -f - </dev/null || true kubectl apply -f- </dev/null | grep -q true; do sleep 5; done"; then + echo "=== DEBUG: Container did not start in time ===" >&2 + kubectl -n tenant-test describe pod openbao-$name-0 >&2 || true + kubectl -n tenant-test logs openbao-$name-0 --previous >&2 || true + kubectl -n tenant-test logs openbao-$name-0 >&2 || true + return 1 + fi + + # Wait for OpenBAO API to accept connections + # bao status exit codes: 0 = unsealed, 1 = error/not ready, 2 = sealed but responsive + if ! timeout 60 sh -ec "until kubectl -n tenant-test exec openbao-$name-0 -- bao status >/dev/null 2>&1; rc=\$?; test \$rc -eq 0 -o \$rc -eq 2; do sleep 3; done"; then + echo "=== DEBUG: OpenBAO API did not become responsive ===" >&2 + kubectl -n tenant-test describe pod openbao-$name-0 >&2 || true + kubectl -n tenant-test logs openbao-$name-0 --previous >&2 || true + kubectl -n tenant-test logs openbao-$name-0 >&2 || true + return 1 + fi + + # Initialize OpenBAO (single key share for testing simplicity) + init_output=$(kubectl -n tenant-test exec openbao-$name-0 -- bao operator init -key-shares=1 -key-threshold=1 -format=json) + unseal_key=$(echo "$init_output" | jq -r '.unseal_keys_b64[0]') + if [ -z "$unseal_key" ] || [ "$unseal_key" = "null" ]; then + echo "Failed to extract unseal key. Init output: $init_output" >&2 + return 1 + fi + + # Unseal OpenBAO + kubectl -n tenant-test exec openbao-$name-0 -- bao operator unseal "$unseal_key" + + # Now wait for pod to become ready (readiness probe checks seal status) + kubectl -n tenant-test wait sts openbao-$name --timeout=90s --for=jsonpath='{.status.readyReplicas}'=1 + kubectl -n tenant-test wait pvc data-openbao-$name-0 --timeout=50s --for=jsonpath='{.status.phase}'=Bound + kubectl -n tenant-test delete openbao.apps.cozystack.io $name + kubectl -n tenant-test delete pvc data-openbao-$name-0 --ignore-not-found +} diff --git a/hack/e2e-apps/postgres.bats b/hack/e2e-apps/postgres.bats index 024ce086..b86bc664 100644 --- a/hack/e2e-apps/postgres.bats +++ b/hack/e2e-apps/postgres.bats @@ -2,6 +2,7 @@ @test "Create DB PostgreSQL" { name='test' + kubectl -n tenant-test delete postgreses.apps.cozystack.io $name --ignore-not-found --timeout=2m || true kubectl apply -f - </dev/null || true + kubectl -n tenant-test wait kuberneteses.apps.cozystack.io "${test_name}" --for=delete --timeout=2m 2>/dev/null || true + kubectl apply -f - < /dev/null 2>&1 &' - # Verify the Kubernetes version matches what we expect (retry for up to 20 seconds) - timeout 20 sh -ec 'until kubectl --kubeconfig tenantkubeconfig-'"${test_name}"' version 2>/dev/null | grep -Fq "Server Version: ${k8s_version}"; do sleep 5; done' + # Kill any stale port-forward on this port from a previous retry + pkill -f "port-forward.*${port}:" 2>/dev/null || true + sleep 1 - # Wait for the nodes to be ready (timeout after 2 minutes) - timeout 3m bash -c ' - until [ "$(kubectl --kubeconfig tenantkubeconfig-'"${test_name}"' get nodes -o jsonpath="{.items[*].metadata.name}" | wc -w)" -eq 2 ]; do + # Set up port forwarding to the Kubernetes API server + # No timeout — process is killed at end of test or by job-level timeout-minutes + kubectl port-forward service/kubernetes-"${test_name}" -n tenant-test "${port}":6443 > /dev/null 2>&1 & + # Wait for port-forward to be ready before using it + timeout 15 sh -ec 'until curl -sk https://localhost:'"${port}"' >/dev/null 2>&1; do sleep 1; done' + # Verify the Kubernetes version matches what we expect (retry for up to 20 seconds) + timeout 20 sh -ec 'until kubectl --kubeconfig tenantkubeconfig-'"${test_name}"' version 2>/dev/null | grep -Fq "Server Version: ${k8s_version}"; do sleep 1; done' + + # Wait for at least 2 nodes to join (timeout after 8 minutes) + timeout 8m bash -c ' + until [ "$(kubectl --kubeconfig tenantkubeconfig-'"${test_name}"' get nodes -o jsonpath="{.items[*].metadata.name}" | wc -w)" -ge 2 ]; do sleep 2 done ' # Verify the nodes are ready - kubectl --kubeconfig "tenantkubeconfig-${test_name}" wait node --all --timeout=2m --for=condition=Ready + if ! kubectl --kubeconfig "tenantkubeconfig-${test_name}" wait node --all --timeout=3m --for=condition=Ready; then + # Dump debug info and fail fast — no point running LB/NFS tests without Ready nodes + kubectl --kubeconfig "tenantkubeconfig-${test_name}" describe nodes + kubectl -n tenant-test get hr + exit 1 + fi kubectl --kubeconfig "tenantkubeconfig-${test_name}" get nodes -o wide # Verify the kubelet version matches what we expect versions=$(kubectl --kubeconfig "tenantkubeconfig-${test_name}" \ get nodes -o jsonpath='{.items[*].status.nodeInfo.kubeletVersion}') - + node_ok=true - + for v in $versions; do case "$v" in "${k8s_version}" | "${k8s_version}".* | "${k8s_version}"-*) @@ -189,7 +207,7 @@ EOF # Wait for pods readiness kubectl wait deployment --kubeconfig "tenantkubeconfig-${test_name}" "${test_name}-backend" -n tenant-test --for=condition=Available --timeout=300s - + # Wait for LoadBalancer to be provisioned (IP or hostname) timeout 90 sh -ec " until kubectl get svc ${test_name}-backend --kubeconfig tenantkubeconfig-${test_name} -n tenant-test \ @@ -198,24 +216,28 @@ EOF done " -LB_ADDR=$( - kubectl get svc --kubeconfig "tenantkubeconfig-${test_name}" "${test_name}-backend" \ - -n tenant-test \ - -o jsonpath='{.status.loadBalancer.ingress[0].ip}{.status.loadBalancer.ingress[0].hostname}' -) + LB_ADDR=$( + kubectl get svc --kubeconfig "tenantkubeconfig-${test_name}" "${test_name}-backend" \ + -n tenant-test \ + -o jsonpath='{.status.loadBalancer.ingress[0].ip}{.status.loadBalancer.ingress[0].hostname}' + ) -if [ -z "$LB_ADDR" ]; then - echo "LoadBalancer address is empty" >&2 - exit 1 -fi + if [ -z "$LB_ADDR" ]; then + echo "LoadBalancer address is empty" >&2 + exit 1 + fi + lb_ok=false for i in $(seq 1 20); do echo "Attempt $i" - curl --silent --fail "http://${LB_ADDR}" && break + if curl --silent --fail "http://${LB_ADDR}"; then + lb_ok=true + break + fi sleep 3 done - if [ "$i" -eq 20 ]; then + if [ "$lb_ok" != true ]; then echo "LoadBalancer not reachable" >&2 exit 1 fi @@ -246,8 +268,8 @@ spec: storage: 1Gi EOF - # Wait for PVC to be bound - kubectl --kubeconfig "tenantkubeconfig-${test_name}" wait pvc nfs-test-pvc -n tenant-test --timeout=2m --for=jsonpath='{.status.phase}'=Bound + # Wait for PVC to be bound (RWX via kubevirt CSI provisions an NFS server pod, needs time) + kubectl --kubeconfig "tenantkubeconfig-${test_name}" wait pvc nfs-test-pvc -n tenant-test --timeout=3m --for=jsonpath='{.status.phase}'=Bound # Create Pod that writes and reads data from NFS volume kubectl --kubeconfig "tenantkubeconfig-${test_name}" apply -f - <&2 + kubectl --kubeconfig "tenantkubeconfig-${test_name}" describe pod nfs-test-pod -n tenant-test >&2 || true + kubectl --kubeconfig "tenantkubeconfig-${test_name}" get events -n tenant-test --sort-by='.lastTimestamp' >&2 || true + exit 1 + fi # Verify NFS data integrity nfs_result=$(kubectl --kubeconfig "tenantkubeconfig-${test_name}" logs nfs-test-pod -n tenant-test) @@ -295,7 +322,38 @@ EOF done kubectl wait hr kubernetes-${test_name}-ingress-nginx -n tenant-test --timeout=5m --for=condition=ready - # Clean up by deleting the Kubernetes resource - kubectl -n tenant-test delete kuberneteses.apps.cozystack.io $test_name + # Guard: parent HelmRelease must not have entered an install/upgrade remediation cycle. + # A non-zero installFailures/upgradeFailures indicates the helm-wait budget expired while + # admin-kubeconfig was still being provisioned, which would trigger uninstall remediation + # and churn the Cluster CR. + # Flux helm-controller v2 retains per-revision release Snapshots in + # .status.history; each Snapshot's .status reflects the Helm release + # state (deployed/superseded/failed/uninstalled). A remediation cycle + # leaves a "failed" or "uninstalled" entry behind that survives a later + # successful reinstall, unlike the installFailures/upgradeFailures + # counters (which ClearFailures zeroes on every successful reconcile). + # The shape is pinned by hack/remediation-guard.bats; the upstream + # types are github.com/fluxcd/helm-controller/api v2 Snapshot. + history_statuses=$(kubectl get hr -n tenant-test "kubernetes-${test_name}" \ + -ojsonpath='{range .status.history[*]}{.status}{"\n"}{end}') + # Always emit the raw value so a silent future-Flux field rename shows + # up as "empty history on a Ready HR" in CI logs rather than vanishing. + echo "Parent HelmRelease history statuses:" + printf '%s\n' "${history_statuses:-}" + if [ -z "${history_statuses}" ]; then + echo "Unexpected empty .status.history on a Ready HelmRelease - Flux API shape may have changed." >&2 + kubectl -n tenant-test describe hr "kubernetes-${test_name}" >&2 + exit 1 + fi + if helmrelease_has_remediation_cycle "${history_statuses}"; then + echo "Parent HelmRelease entered remediation cycle." >&2 + kubectl -n tenant-test describe hr "kubernetes-${test_name}" >&2 + exit 1 + fi + + # Clean up + pkill -f "port-forward.*${port}:" 2>/dev/null || true + rm -f "tenantkubeconfig-${test_name}" + kubectl -n tenant-test delete kuberneteses.apps.cozystack.io "${test_name}" --ignore-not-found --wait=false 2>/dev/null || true } diff --git a/hack/e2e-apps/vminstance.bats b/hack/e2e-apps/vminstance.bats index 60513e8e..fa0f0b5b 100644 --- a/hack/e2e-apps/vminstance.bats +++ b/hack/e2e-apps/vminstance.bats @@ -2,6 +2,8 @@ @test "Create a VM Disk" { name='test' + kubectl -n tenant-test delete vminstances.apps.cozystack.io $name --ignore-not-found --timeout=2m || true + kubectl -n tenant-test delete vmdisks.apps.cozystack.io $name --ignore-not-found --timeout=2m || true kubectl apply -f - </dev/null; do sleep 2; done' + timeout 120 sh -ec 'until kubectl wait crd/packagesources.cozystack.io --for=condition=Established --timeout=10s 2>/dev/null; do sleep 2; done' + + # Wait for operator to create the platform PackageSource + timeout 120 sh -ec 'until kubectl get packagesource cozystack.cozystack-platform >/dev/null 2>&1; do sleep 2; done' + # Create platform Package with isp-full variant kubectl apply -f - </dev/null 2>&1; do sleep 1; done' - kubectl wait deployment/capi-controller-manager deployment/capi-kamaji-controller-manager deployment/capi-kubeadm-bootstrap-controller-manager deployment/capi-operator-cluster-api-operator deployment/capk-controller-manager -n cozy-cluster-api --timeout=1m --for=condition=available + timeout 120 sh -ec 'until kubectl get deploy -n cozy-cluster-api capi-controller-manager capi-kamaji-controller-manager capi-kubeadm-bootstrap-controller-manager capi-operator-cluster-api-operator capk-controller-manager >/dev/null 2>&1; do sleep 1; done' + kubectl wait deployment/capi-controller-manager deployment/capi-kamaji-controller-manager deployment/capi-kubeadm-bootstrap-controller-manager deployment/capi-operator-cluster-api-operator deployment/capk-controller-manager -n cozy-cluster-api --timeout=2m --for=condition=available } @test "Wait for LINSTOR and configure storage" { @@ -167,8 +178,8 @@ EOF # VictoriaMetrics components kubectl wait vmalert/vmalert-shortterm vmalertmanager/alertmanager -n tenant-root --for=jsonpath='{.status.updateStatus}'=operational --timeout=15m - kubectl wait vlogs/generic -n tenant-root --for=jsonpath='{.status.updateStatus}'=operational --timeout=5m - kubectl wait vmcluster/shortterm vmcluster/longterm -n tenant-root --for=jsonpath='{.status.clusterStatus}'=operational --timeout=5m + kubectl wait vlclusters/generic -n tenant-root --for=jsonpath='{.status.updateStatus}'=operational --timeout=5m + kubectl wait vmcluster/shortterm vmcluster/longterm -n tenant-root --for=jsonpath='{.status.updateStatus}'=operational --timeout=5m # Grafana kubectl wait clusters.postgresql.cnpg.io/grafana-db -n tenant-root --for=condition=ready --timeout=5m @@ -189,8 +200,60 @@ EOF kubectl wait hr/keycloak hr/keycloak-configure hr/keycloak-operator -n cozy-keycloak --timeout=10m --for=condition=ready } +@test "Aggregated API rejects Tenant name with dashes" { + # Regression guard: the tenant Helm chart's tenant.name helper splits the + # Release.Name on "-" and fails unless the result is exactly + # ["tenant", ""]. The aggregated API must catch tenant names + # containing dashes up-front with a tenant-specific error, instead of + # silently accepting the Application and letting Flux fail later. + + # Defensive cleanup: if a prior regression left foo-bar in the cluster, + # remove it before exercising the validation so we are not observing + # stale state. Safe even in the happy path because of --ignore-not-found. + kubectl delete tenants.apps.cozystack.io foo-bar -n tenant-root --ignore-not-found + + # Preflight: tenant-root is created by earlier tests in this suite. Fail + # loudly if it is missing so this test does not silently trigger an + # unrelated "namespace not found" error and misreport as a pass. + kubectl get namespace tenant-root + + # --validate=ignore forces kubectl to skip client-side OpenAPI validation + # and send the payload straight to the aggregated API. This guarantees the + # server-side name check runs and the error we grep for is the tenant + # contract error, not a kubectl schema rejection. (--validate=false is the + # deprecated alias.) + local output rc + # Run the apply in its own subshell so we can capture BOTH stdout+stderr + # AND the exit code explicitly, without `|| true` swallowing a real failure + # mode (e.g. network error, auth failure) that should also fail the test. + output=$( + kubectl apply --validate=ignore -f - 2>&1 < /dev/null; then exit 1 fi +# Step 0: Annotate critical resources to prevent Helm from deleting them +echo "Step 0: Protect critical resources from Helm deletion" +echo "" +echo "The following resources will be annotated with helm.sh/resource-policy=keep" +echo "to prevent Helm from deleting them when the installer release is removed:" +echo " - Namespace: $NAMESPACE" +echo " - ConfigMap: $NAMESPACE/cozystack-version" +echo "" +read -p "Do you want to annotate these resources? (y/N) " -n 1 -r +echo "" + +if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "Annotating namespace $NAMESPACE..." + kubectl annotate namespace "$NAMESPACE" helm.sh/resource-policy=keep --overwrite + echo "Annotating ConfigMap cozystack-version..." + kubectl annotate configmap -n "$NAMESPACE" cozystack-version helm.sh/resource-policy=keep --overwrite 2>/dev/null || echo " ConfigMap cozystack-version not found, skipping." + echo "" + echo "Resources annotated successfully." +else + echo "WARNING: Skipping annotation. If you remove the Helm installer release," + echo "the namespace and its contents may be deleted!" +fi +echo "" + +# Step 1: Check for cozy-proxy HelmRelease with conflicting releaseName +# In v0.41.x, cozy-proxy was incorrectly configured with releaseName "cozystack", +# which conflicts with the installer helm release name. If not suspended, cozy-proxy +# HelmRelease will overwrite the installer release and delete cozystack-operator. +COZY_PROXY_RELEASE_NAME=$(kubectl get hr -n "$NAMESPACE" cozy-proxy -o jsonpath='{.spec.releaseName}' 2>/dev/null || true) +if [ "$COZY_PROXY_RELEASE_NAME" = "cozystack" ]; then + echo "WARNING: HelmRelease cozy-proxy has releaseName 'cozystack', which conflicts" + echo "with the installer release. It must be suspended before proceeding, otherwise" + echo "it will overwrite the installer and delete cozystack-operator." + echo "" + read -p "Suspend HelmRelease cozy-proxy? (y/N) " -n 1 -r + echo "" + if [[ $REPLY =~ ^[Yy]$ ]]; then + kubectl -n "$NAMESPACE" patch hr cozy-proxy --type=merge --field-manager=flux-client-side-apply -p '{"spec":{"suspend":true}}' + echo "HelmRelease cozy-proxy suspended." + else + echo "ERROR: Cannot proceed with conflicting cozy-proxy HelmRelease active." + echo "Please suspend it manually:" + echo " kubectl -n $NAMESPACE patch hr cozy-proxy --type=merge -p '{\"spec\":{\"suspend\":true}}'" + exit 1 + fi + echo "" +fi + # Read ConfigMap cozystack echo "Reading ConfigMap cozystack..." COZYSTACK_CM=$(kubectl get configmap -n "$NAMESPACE" cozystack -o json 2>/dev/null || echo "{}") @@ -52,6 +100,43 @@ OIDC_ENABLED=$(echo "$COZYSTACK_CM" | jq -r '.data["oidc-enabled"] // "false"') KEYCLOAK_REDIRECTS=$(echo "$COZYSTACK_CM" | jq -r '.data["extra-keycloak-redirect-uri-for-dashboard"] // ""' ) TELEMETRY_ENABLED=$(echo "$COZYSTACK_CM" | jq -r '.data["telemetry-enabled"] // "true"') BUNDLE_NAME=$(echo "$COZYSTACK_CM" | jq -r '.data["bundle-name"] // "paas-full"') +BUNDLE_DISABLE=$(echo "$COZYSTACK_CM" | jq -r '.data["bundle-disable"] // ""') +BUNDLE_ENABLE=$(echo "$COZYSTACK_CM" | jq -r '.data["bundle-enable"] // ""') +EXPOSE_INGRESS=$(echo "$COZYSTACK_CM" | jq -r '.data["expose-ingress"] // "tenant-root"') +EXPOSE_SERVICES=$(echo "$COZYSTACK_CM" | jq -r '.data["expose-services"] // ""') + +# Certificate issuer configuration (old undocumented field: clusterissuer) +OLD_CLUSTER_ISSUER=$(echo "$COZYSTACK_CM" | jq -r '.data["clusterissuer"] // ""') + +# Convert old clusterissuer value to new solver/issuerName fields +SOLVER="" +ISSUER_NAME="" +case "$OLD_CLUSTER_ISSUER" in + cloudflare) + SOLVER="dns01" + ISSUER_NAME="letsencrypt-prod" + ;; + http01) + SOLVER="http01" + ISSUER_NAME="letsencrypt-prod" + ;; + "") + # Field not set; omit from Package so chart defaults apply + ;; + *) + # Unrecognised value — treat as custom ClusterIssuer name with no solver override + ISSUER_NAME="$OLD_CLUSTER_ISSUER" + ;; +esac + +# Build certificates YAML block (empty string when no override needed) +if [ -n "$SOLVER" ] || [ -n "$ISSUER_NAME" ]; then + CERTIFICATES_SECTION=" certificates: + solver: \"${SOLVER}\" + issuerName: \"${ISSUER_NAME}\"" +else + CERTIFICATES_SECTION="" +fi # Network configuration POD_CIDR=$(echo "$COZYSTACK_CM" | jq -r '.data["ipv4-pod-cidr"] // "10.244.0.0/16"') @@ -66,28 +151,31 @@ else EXTERNAL_IPS=$(echo "$EXTERNAL_IPS" | sed 's/,/\n/g' | awk 'BEGIN{print}{print " - "$0}') fi -# Determine bundle type -case "$BUNDLE_NAME" in - paas-full|distro-full) - SYSTEM_ENABLED="true" - SYSTEM_TYPE="full" - ;; - paas-hosted|distro-hosted) - SYSTEM_ENABLED="false" - SYSTEM_TYPE="hosted" - ;; - *) - SYSTEM_ENABLED="false" - SYSTEM_TYPE="hosted" - ;; -esac +# Convert comma-separated lists to YAML arrays +if [ -z "$BUNDLE_DISABLE" ]; then + DISABLED_PACKAGES="[]" +else + DISABLED_PACKAGES=$(echo "$BUNDLE_DISABLE" | sed 's/,/\n/g' | awk 'BEGIN{print}{print " - cozystack."$0}') +fi + +if [ -z "$BUNDLE_ENABLE" ]; then + ENABLED_PACKAGES="[]" +else + ENABLED_PACKAGES=$(echo "$BUNDLE_ENABLE" | sed 's/,/\n/g' | awk 'BEGIN{print}{print " - cozystack."$0}') +fi + +if [ -z "$EXPOSE_SERVICES" ]; then + EXPOSED_SERVICES_YAML="[]" +else + EXPOSED_SERVICES_YAML=$(echo "$EXPOSE_SERVICES" | sed 's/,/\n/g' | awk 'BEGIN{print}{print " - "$0}') +fi # Update bundle naming BUNDLE_NAME=$(echo "$BUNDLE_NAME" | sed 's/paas/isp/') # Extract branding if available BRANDING=$(echo "$BRANDING_CM" | jq -r '.data // {} | to_entries[] | "\(.key): \"\(.value)\""') -if [ -z "$BRANDING" ]; then +if [ -z "$BRANDING" ]; then BRANDING="{}" else BRANDING=$(echo "$BRANDING" | awk 'BEGIN{print}{print " " $0}') @@ -108,8 +196,8 @@ echo " Root Host: $ROOT_HOST" echo " API Server Endpoint: $API_SERVER_ENDPOINT" echo " OIDC Enabled: $OIDC_ENABLED" echo " Bundle Name: $BUNDLE_NAME" -echo " System Enabled: $SYSTEM_ENABLED" -echo " System Type: $SYSTEM_TYPE" +echo " Certificate Solver: ${SOLVER:-http01 (default)}" +echo " Issuer Name: ${ISSUER_NAME:-letsencrypt-prod (default)}" echo "" # Generate Package YAML @@ -125,15 +213,8 @@ spec: platform: values: bundles: - system: - enabled: $SYSTEM_ENABLED - type: "$SYSTEM_TYPE" - iaas: - enabled: true - paas: - enabled: true - naas: - enabled: true + disabledPackages: $DISABLED_PACKAGES + enabledPackages: $ENABLED_PACKAGES networking: clusterDomain: "$CLUSTER_DOMAIN" podCIDR: "$POD_CIDR" @@ -142,8 +223,11 @@ spec: joinCIDR: "$JOIN_CIDR" publishing: host: "$ROOT_HOST" + ingressName: "$EXPOSE_INGRESS" + exposedServices: $EXPOSED_SERVICES_YAML apiServerEndpoint: "$API_SERVER_ENDPOINT" externalIPs: $EXTERNAL_IPS +${CERTIFICATES_SECTION} authentication: oidc: enabled: $OIDC_ENABLED diff --git a/hack/remediation-guard.bats b/hack/remediation-guard.bats new file mode 100644 index 00000000..092fe06d --- /dev/null +++ b/hack/remediation-guard.bats @@ -0,0 +1,127 @@ +#!/usr/bin/env bats +# ----------------------------------------------------------------------------- +# Unit tests for hack/e2e-apps/remediation-guard.sh +# +# helmrelease_has_remediation_cycle takes a newline-delimited list of +# HelmRelease history snapshot status values (deployed/superseded/failed/ +# uninstalled/...) and returns 0 when any entry is "failed" or "uninstalled" +# (meaning flux helm-controller performed install/upgrade remediation). +# +# This is used by the e2e script after the HelmRelease reaches Ready. The +# failure/upgrade counters (.status.installFailures / .status.upgradeFailures) +# are useless there because flux's ClearFailures zeroes them on successful +# reconciliation; .status.history retains the snapshot trail. +# +# cozytest.sh's awk parser recognizes only @test blocks and a bare `}` on +# its own line; there is no bats `run` or `$status`. Assertions are +# expressed as direct shell tests that exit non-zero on failure. +# +# Run with: hack/cozytest.sh hack/remediation-guard.bats +# ----------------------------------------------------------------------------- + +@test "empty history returns not-detected" { + . hack/e2e-apps/remediation-guard.sh + if helmrelease_has_remediation_cycle ""; then + echo "expected not-detected for empty history" >&2 + exit 1 + fi +} + +@test "single deployed snapshot returns not-detected" { + . hack/e2e-apps/remediation-guard.sh + if helmrelease_has_remediation_cycle "deployed"; then + echo "expected not-detected for deployed-only history" >&2 + exit 1 + fi +} + +@test "deployed then superseded returns not-detected" { + . hack/e2e-apps/remediation-guard.sh + statuses=$(printf 'deployed\nsuperseded\n') + if helmrelease_has_remediation_cycle "${statuses}"; then + echo "expected not-detected for deployed+superseded history" >&2 + exit 1 + fi +} + +@test "single failed snapshot returns detected" { + . hack/e2e-apps/remediation-guard.sh + if ! helmrelease_has_remediation_cycle "failed"; then + echo "expected detected when history contains failed snapshot" >&2 + exit 1 + fi +} + +@test "single uninstalled snapshot returns detected" { + # The exact signature of the install-remediation race: the first install + # exceeded flux's wait budget, remediation uninstalled, the next retry + # eventually succeeded. History still carries the uninstalled snapshot. + . hack/e2e-apps/remediation-guard.sh + if ! helmrelease_has_remediation_cycle "uninstalled"; then + echo "expected detected when history contains uninstalled snapshot" >&2 + exit 1 + fi +} + +@test "uninstalled then deployed still returns detected" { + . hack/e2e-apps/remediation-guard.sh + statuses=$(printf 'uninstalled\ndeployed\n') + if ! helmrelease_has_remediation_cycle "${statuses}"; then + echo "expected detected despite later successful deploy" >&2 + exit 1 + fi +} + +@test "deployed then failed still returns detected" { + . hack/e2e-apps/remediation-guard.sh + statuses=$(printf 'deployed\nfailed\n') + if ! helmrelease_has_remediation_cycle "${statuses}"; then + echo "expected detected when any entry is failed" >&2 + exit 1 + fi +} + +@test "status.history extraction pins HR v2 status.history shape" { + # Pins the Flux HelmRelease v2 .status.history[].status shape that + # run-kubernetes.sh relies on. If a future flux release renames the + # field, the jsonpath returns nothing, the guard reports no cycle, + # and real remediation loops slip past the e2e assertion. This test + # uses yq to read the exact path used in the e2e script; the upstream + # Snapshot type lives at + # github.com/fluxcd/helm-controller/api/v2.Snapshot (via go.mod). + tmp=$(mktemp -d) + trap 'rm -rf "$tmp"' EXIT + + cat > "$tmp/hr.yaml" <<'YAML' +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: kubernetes-test + namespace: tenant-test +status: + history: + - name: kubernetes-test + namespace: tenant-test + version: 1 + status: uninstalled + - name: kubernetes-test + namespace: tenant-test + version: 2 + status: deployed +YAML + + # Default yq output is yaml scalar format, which for string values emits + # bare unquoted tokens - matching what kubectl -o jsonpath produces in + # e2e. Do not switch to JSON output here; that would quote the values + # and break the loop in helmrelease_has_remediation_cycle. + statuses=$(yq '.status.history[].status' "$tmp/hr.yaml") + + [ -n "$statuses" ] + echo "$statuses" | grep --quiet '^uninstalled$' + + . hack/e2e-apps/remediation-guard.sh + if ! helmrelease_has_remediation_cycle "$statuses"; then + echo "expected detected for pinned HR snippet with uninstalled + deployed history" >&2 + exit 1 + fi +} diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh index b7f96691..f3a47e78 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -24,8 +24,7 @@ API_KNOWN_VIOLATIONS_DIR="${API_KNOWN_VIOLATIONS_DIR:-"${SCRIPT_ROOT}/api/api-ru UPDATE_API_KNOWN_VIOLATIONS="${UPDATE_API_KNOWN_VIOLATIONS:-true}" CONTROLLER_GEN="go run sigs.k8s.io/controller-tools/cmd/controller-gen@v0.16.4" TMPDIR=$(mktemp -d) -OPERATOR_CRDDIR=packages/core/installer/crds -OPERATOR_EMBEDDIR=internal/crdinstall/manifests +OPERATOR_CRDDIR=internal/crdinstall/manifests COZY_CONTROLLER_CRDDIR=packages/system/cozystack-controller/definitions COZY_RD_CRDDIR=packages/system/application-definition-crd/definition BACKUPS_CORE_CRDDIR=packages/system/backup-controller/definitions @@ -69,14 +68,11 @@ kube::codegen::gen_client \ "${SCRIPT_ROOT}/pkg/apis" $CONTROLLER_GEN object:headerFile="hack/boilerplate.go.txt" paths="./api/..." -$CONTROLLER_GEN rbac:roleName=manager-role crd paths="./api/..." output:crd:artifacts:config=${TMPDIR} +$CONTROLLER_GEN rbac:roleName=manager-role crd paths="./api/v1alpha1/..." paths="./api/backups/..." paths="./api/dashboard/..." output:crd:artifacts:config=${TMPDIR} mv ${TMPDIR}/cozystack.io_packages.yaml ${OPERATOR_CRDDIR}/cozystack.io_packages.yaml mv ${TMPDIR}/cozystack.io_packagesources.yaml ${OPERATOR_CRDDIR}/cozystack.io_packagesources.yaml -cp ${OPERATOR_CRDDIR}/cozystack.io_packages.yaml ${OPERATOR_EMBEDDIR}/cozystack.io_packages.yaml -cp ${OPERATOR_CRDDIR}/cozystack.io_packagesources.yaml ${OPERATOR_EMBEDDIR}/cozystack.io_packagesources.yaml - mv ${TMPDIR}/cozystack.io_applicationdefinitions.yaml \ ${COZY_RD_CRDDIR}/cozystack.io_applicationdefinitions.yaml @@ -84,3 +80,12 @@ mv ${TMPDIR}/backups.cozystack.io*.yaml ${BACKUPS_CORE_CRDDIR}/ mv ${TMPDIR}/strategy.backups.cozystack.io*.yaml ${BACKUPSTRATEGY_CRDDIR}/ mv ${TMPDIR}/*.yaml ${COZY_CONTROLLER_CRDDIR}/ + +# Tidy dependencies for standalone api/apps/v1alpha1 submodule +(cd "${SCRIPT_ROOT}/api/apps/v1alpha1" && go mod tidy) + +# Generate deepcopy for standalone api/apps/v1alpha1 submodule (separate Go module) +# Use absolute path for headerFile since we cd into the submodule directory +APPS_API_ROOT="$(cd "${SCRIPT_ROOT}" && pwd)" +(cd "${APPS_API_ROOT}/api/apps/v1alpha1" && $CONTROLLER_GEN object:headerFile="${APPS_API_ROOT}/hack/boilerplate.go.txt" paths="./...") + diff --git a/hack/upload-assets.sh b/hack/upload-assets.sh index f9bb7b25..2f7f9813 100755 --- a/hack/upload-assets.sh +++ b/hack/upload-assets.sh @@ -14,3 +14,4 @@ gh release upload --clobber $version _out/assets/kernel-amd64 gh release upload --clobber $version _out/assets/initramfs-metal-amd64.xz gh release upload --clobber $version _out/assets/cozypkg-*.tar.gz gh release upload --clobber $version _out/assets/cozypkg-checksums.txt +gh release upload --clobber $version _out/assets/openapi.json diff --git a/internal/backupcontroller/backup_controller.go b/internal/backupcontroller/backup_controller.go new file mode 100644 index 00000000..486a24ae --- /dev/null +++ b/internal/backupcontroller/backup_controller.go @@ -0,0 +1,136 @@ +package backupcontroller + +import ( + "context" + "fmt" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/log" + + backupsv1alpha1 "github.com/cozystack/cozystack/api/backups/v1alpha1" + velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +const backupFinalizer = "backups.cozystack.io/cleanup-velero" + +// BackupReconciler reconciles Backup objects. +// It manages a finalizer that ensures the underlying Velero backup is deleted +// when the cozystack Backup resource is deleted. +type BackupReconciler struct { + client.Client + Scheme *runtime.Scheme + Recorder record.EventRecorder +} + +func (r *BackupReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + logger.V(1).Info("reconciling Backup", "namespace", req.Namespace, "name", req.Name) + + backup := &backupsv1alpha1.Backup{} + if err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: req.Name}, backup); err != nil { + if apierrors.IsNotFound(err) { + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + + // Handle deletion: clean up Velero backup + if !backup.DeletionTimestamp.IsZero() { + if controllerutil.ContainsFinalizer(backup, backupFinalizer) { + if err := r.cleanupVeleroBackup(ctx, backup); err != nil { + logger.Error(err, "failed to clean up Velero backup") + return ctrl.Result{}, err + } + + controllerutil.RemoveFinalizer(backup, backupFinalizer) + if err := r.Update(ctx, backup); err != nil { + return ctrl.Result{}, err + } + logger.V(1).Info("removed finalizer and cleaned up Velero backup", "backup", backup.Name) + } + return ctrl.Result{}, nil + } + + // Ensure finalizer is present + if !controllerutil.ContainsFinalizer(backup, backupFinalizer) { + controllerutil.AddFinalizer(backup, backupFinalizer) + if err := r.Update(ctx, backup); err != nil { + return ctrl.Result{}, err + } + logger.V(1).Info("added finalizer to Backup", "backup", backup.Name) + } + + return ctrl.Result{}, nil +} + +// cleanupVeleroBackup deletes the Velero backup and its data from storage +// by creating a Velero DeleteBackupRequest. A direct Delete of the +// backup.velero.io resource only removes the Kubernetes object; Velero's +// BSL sync will recreate it from the object store. The DeleteBackupRequest +// tells Velero to also purge the data, preventing resurrection. +func (r *BackupReconciler) cleanupVeleroBackup(ctx context.Context, backup *backupsv1alpha1.Backup) error { + logger := log.FromContext(ctx) + + veleroBackupName, ok := backup.Spec.DriverMetadata[veleroBackupNameMetadataKey] + if !ok || veleroBackupName == "" { + logger.V(1).Info("no Velero backup name in driverMetadata, nothing to clean up") + return nil + } + + veleroBackupNamespace := backup.Spec.DriverMetadata[veleroBackupNamespaceMetadataKey] + if veleroBackupNamespace == "" { + veleroBackupNamespace = veleroNamespace + } + + // Check if the Velero Backup still exists + veleroBackup := &velerov1.Backup{} + err := r.Get(ctx, types.NamespacedName{ + Namespace: veleroBackupNamespace, + Name: veleroBackupName, + }, veleroBackup) + if err != nil { + if apierrors.IsNotFound(err) { + logger.V(1).Info("Velero backup already deleted", "name", veleroBackupName) + return nil + } + return fmt.Errorf("failed to get Velero backup %s/%s: %w", veleroBackupNamespace, veleroBackupName, err) + } + + // Create a DeleteBackupRequest so Velero removes backup data from storage. + // Without this, BSL sync will recreate the backup.velero.io resource. + dbr := &velerov1.DeleteBackupRequest{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: veleroBackupName + "-", + Namespace: veleroBackupNamespace, + }, + Spec: velerov1.DeleteBackupRequestSpec{ + BackupName: veleroBackupName, + }, + } + if err := r.Create(ctx, dbr); err != nil { + if apierrors.IsAlreadyExists(err) { + logger.V(1).Info("DeleteBackupRequest already exists", "backup", veleroBackupName) + return nil + } + return fmt.Errorf("failed to create DeleteBackupRequest for %s/%s: %w", veleroBackupNamespace, veleroBackupName, err) + } + + logger.Info("created DeleteBackupRequest for Velero backup", + "name", veleroBackupName, "namespace", veleroBackupNamespace, + "deleteRequest", dbr.Name) + return nil +} + +// SetupWithManager registers the BackupReconciler with the Manager. +func (r *BackupReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&backupsv1alpha1.Backup{}). + Complete(r) +} diff --git a/internal/backupcontroller/backupclass_resolver_test.go b/internal/backupcontroller/backupclass_resolver_test.go index 5e29cd02..cd65f22a 100644 --- a/internal/backupcontroller/backupclass_resolver_test.go +++ b/internal/backupcontroller/backupclass_resolver_test.go @@ -369,7 +369,3 @@ func TestResolveBackupClass(t *testing.T) { }) } } - -func stringPtr(s string) *string { - return &s -} diff --git a/internal/backupcontroller/restorejob_controller.go b/internal/backupcontroller/restorejob_controller.go index 71307ba3..f0062064 100644 --- a/internal/backupcontroller/restorejob_controller.go +++ b/internal/backupcontroller/restorejob_controller.go @@ -17,12 +17,16 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/log" strategyv1alpha1 "github.com/cozystack/cozystack/api/backups/strategy/v1alpha1" backupsv1alpha1 "github.com/cozystack/cozystack/api/backups/v1alpha1" + velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" ) +const restoreJobFinalizer = "backups.cozystack.io/cleanup-velero-restore" + // RestoreJobReconciler reconciles RestoreJob objects. // It routes RestoreJobs to strategy-specific handlers based on the strategy // referenced in the Backup that the RestoreJob is restoring from. @@ -49,6 +53,27 @@ func (r *RestoreJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, err } + // Handle deletion: clean up Velero Restore + if !restoreJob.DeletionTimestamp.IsZero() { + if controllerutil.ContainsFinalizer(restoreJob, restoreJobFinalizer) { + r.cleanupVeleroRestore(ctx, restoreJob) + controllerutil.RemoveFinalizer(restoreJob, restoreJobFinalizer) + if err := r.Update(ctx, restoreJob); err != nil { + return ctrl.Result{}, err + } + logger.V(1).Info("removed finalizer and cleaned up Velero Restore", "restoreJob", restoreJob.Name) + } + return ctrl.Result{}, nil + } + + // Ensure finalizer is present + if !controllerutil.ContainsFinalizer(restoreJob, restoreJobFinalizer) { + controllerutil.AddFinalizer(restoreJob, restoreJobFinalizer) + if err := r.Update(ctx, restoreJob); err != nil { + return ctrl.Result{}, err + } + } + // If already completed, no need to reconcile if restoreJob.Status.Phase == backupsv1alpha1.RestoreJobPhaseSucceeded || restoreJob.Status.Phase == backupsv1alpha1.RestoreJobPhaseFailed { @@ -103,17 +128,6 @@ func (r *RestoreJobReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } -// getTargetApplicationRef determines the effective target application reference. -// According to DESIGN.md, if spec.targetApplicationRef is omitted, drivers SHOULD -// restore into backup.spec.applicationRef. -// The returned reference is normalized to ensure APIGroup has a default value. -func (r *RestoreJobReconciler) getTargetApplicationRef(restoreJob *backupsv1alpha1.RestoreJob, backup *backupsv1alpha1.Backup) corev1.TypedLocalObjectReference { - if restoreJob.Spec.TargetApplicationRef != nil { - return backupsv1alpha1.NormalizeApplicationRef(*restoreJob.Spec.TargetApplicationRef) - } - return backup.Spec.ApplicationRef -} - // markRestoreJobFailed updates the RestoreJob status to Failed with the given message. func (r *RestoreJobReconciler) markRestoreJobFailed(ctx context.Context, restoreJob *backupsv1alpha1.RestoreJob, message string) (ctrl.Result, error) { logger := getLogger(ctx) @@ -138,3 +152,43 @@ func (r *RestoreJobReconciler) markRestoreJobFailed(ctx context.Context, restore logger.Debug("RestoreJob failed", "message", message) return ctrl.Result{}, nil } + +// cleanupResourceModifierConfigMaps deletes resource modifier ConfigMaps owned +// by this RestoreJob. Called on completion (success or failure) to avoid leaking +// ConfigMaps in cozy-velero when RestoreJobs are not immediately deleted. +func (r *RestoreJobReconciler) cleanupResourceModifierConfigMaps(ctx context.Context, restoreJob *backupsv1alpha1.RestoreJob) { + logger := log.FromContext(ctx) + opts := []client.DeleteAllOfOption{ + client.InNamespace(veleroNamespace), + client.MatchingLabels{ + backupsv1alpha1.OwningJobNameLabel: restoreJob.Name, + backupsv1alpha1.OwningJobNamespaceLabel: restoreJob.Namespace, + }, + } + if err := r.DeleteAllOf(ctx, &corev1.ConfigMap{}, opts...); err != nil { + logger.Error(err, "failed to clean up resourceModifiers ConfigMap(s)") + } +} + +// cleanupVeleroRestore deletes all Velero Restores and resourceModifier +// ConfigMaps owned by this RestoreJob (identified by labels). +func (r *RestoreJobReconciler) cleanupVeleroRestore(ctx context.Context, restoreJob *backupsv1alpha1.RestoreJob) { + logger := log.FromContext(ctx) + opts := []client.DeleteAllOfOption{ + client.InNamespace(veleroNamespace), + client.MatchingLabels{ + backupsv1alpha1.OwningJobNameLabel: restoreJob.Name, + backupsv1alpha1.OwningJobNamespaceLabel: restoreJob.Namespace, + }, + } + + if err := r.DeleteAllOf(ctx, &velerov1.Restore{}, opts...); err != nil { + logger.Error(err, "failed to delete Velero Restore(s)") + r.Recorder.Event(restoreJob, corev1.EventTypeWarning, "CleanupFailed", + fmt.Sprintf("Failed to delete Velero Restore: %v", err)) + } + + if err := r.DeleteAllOf(ctx, &corev1.ConfigMap{}, opts...); err != nil { + logger.Error(err, "failed to delete resourceModifiers ConfigMap(s)") + } +} diff --git a/internal/backupcontroller/velerostrategy_controller.go b/internal/backupcontroller/velerostrategy_controller.go index 6809bd4f..8b063519 100644 --- a/internal/backupcontroller/velerostrategy_controller.go +++ b/internal/backupcontroller/velerostrategy_controller.go @@ -2,13 +2,21 @@ package backupcontroller import ( "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" "fmt" + "strings" "time" + "sigs.k8s.io/yaml" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -37,15 +45,6 @@ func (l loggerWithDebug) Debug(msg string, keysAndValues ...interface{}) { l.Logger.V(1).Info(msg, keysAndValues...) } -// S3Credentials holds the discovered S3 credentials from a Bucket storageRef -type S3Credentials struct { - BucketName string - Endpoint string - Region string - AccessKeyID string - AccessSecretKey string -} - const ( defaultRequeueAfter = 5 * time.Second defaultActiveJobPollingInterval = defaultRequeueAfter @@ -55,12 +54,163 @@ const ( veleroNamespace = "cozy-velero" veleroBackupNameMetadataKey = "velero.io/backup-name" veleroBackupNamespaceMetadataKey = "velero.io/backup-namespace" + + // Annotation key for persisting underlying resources on the Velero Backup object + underlyingResourcesAnnotation = "backups.cozystack.io/underlying-resources" + + // VM-specific constants + vmInstanceKind = "VMInstance" + vmDiskAppKind = "VMDisk" + vmNamePrefix = "vm-instance-" + vmDiskNamePrefix = "vm-disk-" + appKindLabel = "apps.cozystack.io/application.kind" + appNameLabel = "apps.cozystack.io/application.name" + vmPodNameLabel = "vm.kubevirt.io/name" + ovnIPAnnotation = "ovn.kubernetes.io/ip_address" + ovnMACAnnotation = "ovn.kubernetes.io/mac_address" + cdiAllowClaimAdoption = "cdi.kubevirt.io/allowClaimAdoption" ) +func stringPtr(s string) *string { + return &s +} + +// boolPtr returns a pointer to a bool value. func boolPtr(b bool) *bool { return &b } +// boolDefault returns the value of a *bool pointer, or the given default if nil. +func boolDefault(p *bool, def bool) bool { + if p != nil { + return *p + } + return def +} + +// CommonRestoreOptions contains driver-agnostic restore options shared across +// all application kinds. +type CommonRestoreOptions struct { + // TargetNamespace is the namespace to restore into. When set (and differs + // from the backup namespace), a cross-namespace restore (copy) is performed + // using Velero's namespaceMapping. + TargetNamespace string `json:"targetNamespace,omitempty"` + // FailIfTargetExists makes the restore fail if the target resource already + // exists. Defaults to true when omitted. + FailIfTargetExists *bool `json:"failIfTargetExists,omitempty"` +} + +// RestoreOptions is the typed representation of RestoreJob.Spec.Options for the +// Velero driver. The struct is deserialized from runtime.RawExtension and used +// for all application kinds. VMInstance-specific fields (KeepOriginalPVC, +// KeepOriginalIpAndMac) are only effective when the application kind is VMInstance. +type RestoreOptions struct { + CommonRestoreOptions `json:",inline"` + // KeepOriginalPVC renames the original PVC to -orig- before restore. + // Only effective for in-place VMInstance restore (no targetNamespace). Defaults to true when omitted. + KeepOriginalPVC *bool `json:"keepOriginalPVC,omitempty"` + // KeepOriginalIpAndMac preserves the original IP and MAC address via OVN + // annotations. Only effective for VMInstance restores. Defaults to true when omitted. + KeepOriginalIpAndMac *bool `json:"keepOriginalIpAndMac,omitempty"` +} + +// GetFailIfTargetExists returns the effective value (default: true). +func (o *CommonRestoreOptions) GetFailIfTargetExists() bool { + return boolDefault(o.FailIfTargetExists, true) +} + +// GetKeepOriginalPVC returns the effective value (default: true). +func (o *RestoreOptions) GetKeepOriginalPVC() bool { + return boolDefault(o.KeepOriginalPVC, true) +} + +// GetKeepOriginalIpAndMac returns the effective value (default: true). +func (o *RestoreOptions) GetKeepOriginalIpAndMac() bool { + return boolDefault(o.KeepOriginalIpAndMac, true) +} + +// parseRestoreOptions deserializes RestoreJob.Spec.Options into RestoreOptions. +// Returns zero-value RestoreOptions if options is nil. +func parseRestoreOptions(opts *runtime.RawExtension) (RestoreOptions, error) { + var ro RestoreOptions + if opts == nil || len(opts.Raw) == 0 { + return ro, nil + } + if err := json.Unmarshal(opts.Raw, &ro); err != nil { + return ro, fmt.Errorf("failed to parse restore options: %w", err) + } + return ro, nil +} + +// restoreTarget holds the resolved target namespace and app identity for a restore operation. +type restoreTarget struct { + Namespace string + AppName string + AppKind string + IsCopy bool // true when targetNamespace differs from backup namespace + IsRenamed bool // true when target app name differs from source app name +} + +// resolveRestoreTarget computes the effective restore target from RestoreJob, Backup, and options. +func resolveRestoreTarget(restoreJob *backupsv1alpha1.RestoreJob, backup *backupsv1alpha1.Backup, opts RestoreOptions) restoreTarget { + targetNS := backup.Namespace + isCopy := false + if opts.TargetNamespace != "" && opts.TargetNamespace != backup.Namespace { + targetNS = opts.TargetNamespace + isCopy = true + } + targetAppName := backup.Spec.ApplicationRef.Name + if restoreJob.Spec.TargetApplicationRef != nil && restoreJob.Spec.TargetApplicationRef.Name != "" { + targetAppName = restoreJob.Spec.TargetApplicationRef.Name + } + targetAppKind := backup.Spec.ApplicationRef.Kind + if restoreJob.Spec.TargetApplicationRef != nil && restoreJob.Spec.TargetApplicationRef.Kind != "" { + targetAppKind = restoreJob.Spec.TargetApplicationRef.Kind + } + return restoreTarget{ + Namespace: targetNS, + AppName: targetAppName, + AppKind: targetAppKind, + IsCopy: isCopy, + IsRenamed: targetAppName != backup.Spec.ApplicationRef.Name, + } +} + +// vmInstanceResources contains VM-specific underlying resources discovered during backup. +type vmInstanceResources struct { + DataVolumes []backupsv1alpha1.DataVolumeResource `json:"dataVolumes,omitempty"` + IP string `json:"ip,omitempty"` + MAC string `json:"mac,omitempty"` +} + +// marshalUnderlyingResources serializes application-specific data into a +// runtime.RawExtension suitable for Backup.Status.UnderlyingResources. +func marshalUnderlyingResources(data interface{}) (*runtime.RawExtension, error) { + raw, err := json.Marshal(data) + if err != nil { + return nil, err + } + return &runtime.RawExtension{Raw: raw}, nil +} + +// getVMInstanceResources extracts VMInstance-specific resources from the opaque blob. +// The caller is responsible for checking that the application kind is VMInstance +// (via backup.Spec.ApplicationRef.Kind) before calling this function. +// Returns nil if ur is nil or has no VM-specific data. +func getVMInstanceResources(ur *runtime.RawExtension) *vmInstanceResources { + if ur == nil || len(ur.Raw) == 0 { + return nil + } + var res vmInstanceResources + if err := json.Unmarshal(ur.Raw, &res); err != nil { + return nil + } + if len(res.DataVolumes) == 0 && res.IP == "" && res.MAC == "" { + return nil + } + return &res +} + func (r *BackupJobReconciler) reconcileVelero(ctx context.Context, j *backupsv1alpha1.BackupJob, resolved *ResolvedBackupConfig) (ctrl.Result, error) { logger := getLogger(ctx) logger.Debug("reconciling Velero strategy", "backupjob", j.Name, "phase", j.Status.Phase) @@ -197,10 +347,7 @@ func (r *BackupJobReconciler) reconcileVelero(ctx context.Context, j *backupsv1a // Step 5: On failure if phase == "Failed" || phase == "PartiallyFailed" { - message := fmt.Sprintf("Velero Backup failed with phase: %s", phase) - if len(veleroBackup.Status.ValidationErrors) > 0 { - message = fmt.Sprintf("%s: %v", message, veleroBackup.Status.ValidationErrors) - } + message := formatVeleroBackupFailureMessageForBackupJob(ctx, r.Client, veleroBackup) return r.markBackupJobFailed(ctx, j, message) } @@ -208,6 +355,76 @@ func (r *BackupJobReconciler) reconcileVelero(ctx context.Context, j *backupsv1a return ctrl.Result{RequeueAfter: 5 * time.Second}, nil } +// collectUnderlyingResources discovers resources associated with an application +// that need to be backed up and restored. Returns a map keyed by application kind. +// Returns nil if the application type has no underlying resources to collect. +func (r *BackupJobReconciler) collectUnderlyingResources(ctx context.Context, app *unstructured.Unstructured, appKind, ns string) (*runtime.RawExtension, error) { + logger := getLogger(ctx) + + if appKind != vmInstanceKind { + logger.Debug("application is not a VMInstance, skipping underlying resource collection", "kind", appKind) + return nil, nil + } + + appName := app.GetName() + + // Extract disk names from VMInstance spec.disks[].name + disks, found, err := unstructured.NestedSlice(app.Object, "spec", "disks") + if err != nil { + return nil, fmt.Errorf("failed to read spec.disks from application: %w", err) + } + + var dataVolumes []backupsv1alpha1.DataVolumeResource + if found { + for _, d := range disks { + disk, ok := d.(map[string]interface{}) + if !ok { + continue + } + name, ok := disk["name"].(string) + if !ok || name == "" { + continue + } + dataVolumes = append(dataVolumes, backupsv1alpha1.DataVolumeResource{ + DataVolumeName: vmDiskNamePrefix + name, + ApplicationName: name, + }) + } + } + logger.Debug("collected dataVolumes from VMInstance", "count", len(dataVolumes), "appName", appName) + + // Find VM Pod to extract OVN IP/MAC addresses + vmName := vmNamePrefix + appName + podList := &corev1.PodList{} + if err := r.List(ctx, podList, + client.InNamespace(ns), + client.MatchingLabels{vmPodNameLabel: vmName}, + ); err != nil { + logger.Error(err, "failed to list VM pods for IP/MAC collection", "vmName", vmName) + // Non-fatal: we can still proceed without IP/MAC + } + + var ip, mac string + if len(podList.Items) > 0 { + pod := podList.Items[0] + ip = pod.Annotations[ovnIPAnnotation] + mac = pod.Annotations[ovnMACAnnotation] + logger.Debug("collected OVN network info from VM pod", "ip", ip, "mac", mac, "pod", pod.Name) + } else { + logger.Debug("no VM pod found for OVN info", "vmName", vmName) + } + + if len(dataVolumes) == 0 && ip == "" && mac == "" { + return nil, nil + } + + return marshalUnderlyingResources(vmInstanceResources{ + DataVolumes: dataVolumes, + IP: ip, + MAC: mac, + }) +} + func (r *BackupJobReconciler) createVeleroBackup(ctx context.Context, backupJob *backupsv1alpha1.BackupJob, strategy *strategyv1alpha1.Velero, resolved *ResolvedBackupConfig) error { logger := getLogger(ctx) logger.Debug("createVeleroBackup called", "strategy", strategy.Name) @@ -225,6 +442,13 @@ func (r *BackupJobReconciler) createVeleroBackup(ctx context.Context, backupJob return err } + // Collect underlying resources (VM disks, IP/MAC) + underlyingResources, err := r.collectUnderlyingResources(ctx, app, backupJob.Spec.ApplicationRef.Kind, backupJob.Namespace) + if err != nil { + logger.Error(err, "failed to collect underlying resources, proceeding without them") + // Non-fatal: proceed with backup even if collection fails + } + templateContext := map[string]interface{}{ "Application": app.Object, "Parameters": resolved.Parameters, @@ -234,6 +458,28 @@ func (r *BackupJobReconciler) createVeleroBackup(ctx context.Context, backupJob if err != nil { return err } + + // Add label selectors for underlying VMDisk HelmReleases + if vmRes := getVMInstanceResources(underlyingResources); vmRes != nil { + for _, dv := range vmRes.DataVolumes { + veleroBackupSpec.OrLabelSelectors = append(veleroBackupSpec.OrLabelSelectors, &metav1.LabelSelector{ + MatchLabels: map[string]string{ + appKindLabel: vmDiskAppKind, + appNameLabel: dv.ApplicationName, + }, + }) + } + if len(vmRes.DataVolumes) > 0 { + logger.Debug("added VMDisk label selectors to Velero backup", "count", len(vmRes.DataVolumes)) + } + } + + // Serialize underlying resources as annotation to persist across reconcile cycles + annotations := map[string]string{} + if underlyingResources != nil && len(underlyingResources.Raw) > 0 { + annotations[underlyingResourcesAnnotation] = string(underlyingResources.Raw) + } + veleroBackup := &velerov1.Backup{ ObjectMeta: metav1.ObjectMeta{ GenerateName: fmt.Sprintf("%s.%s-", backupJob.Namespace, backupJob.Name), @@ -242,6 +488,7 @@ func (r *BackupJobReconciler) createVeleroBackup(ctx context.Context, backupJob backupsv1alpha1.OwningJobNameLabel: backupJob.Name, backupsv1alpha1.OwningJobNamespaceLabel: backupJob.Namespace, }, + Annotations: annotations, }, Spec: *veleroBackupSpec, } @@ -284,19 +531,18 @@ func (r *BackupJobReconciler) createBackupResource(ctx context.Context, backupJo URI: fmt.Sprintf("velero://%s/%s", veleroBackup.Namespace, veleroBackup.Name), } + // Read underlying resources from Velero Backup annotation + var underlyingResources *runtime.RawExtension + if urJSON, ok := veleroBackup.Annotations[underlyingResourcesAnnotation]; ok && urJSON != "" { + underlyingResources = &runtime.RawExtension{Raw: []byte(urJSON)} + } + + // Note: No OwnerReferences set on Backup. The Backup must survive BackupJob deletion + // so users don't lose their backup artifacts when cleaning up completed jobs. backup := &backupsv1alpha1.Backup{ ObjectMeta: metav1.ObjectMeta{ Name: backupJob.Name, Namespace: backupJob.Namespace, - OwnerReferences: []metav1.OwnerReference{ - { - APIVersion: backupJob.APIVersion, - Kind: backupJob.Kind, - Name: backupJob.Name, - UID: backupJob.UID, - Controller: boolPtr(true), - }, - }, }, Spec: backupsv1alpha1.BackupSpec{ ApplicationRef: backupJob.Spec.ApplicationRef, @@ -305,8 +551,9 @@ func (r *BackupJobReconciler) createBackupResource(ctx context.Context, backupJo DriverMetadata: driverMetadata, }, Status: backupsv1alpha1.BackupStatus{ - Phase: backupsv1alpha1.BackupPhaseReady, - Artifact: artifact, + Phase: backupsv1alpha1.BackupPhaseReady, + Artifact: artifact, + UnderlyingResources: underlyingResources, }, } @@ -319,7 +566,8 @@ func (r *BackupJobReconciler) createBackupResource(ctx context.Context, backupJo return nil, err } - logger.Debug("created Backup resource", "name", backup.Name) + logger.Debug("created Backup resource", "name", backup.Name, + "hasUnderlyingResources", underlyingResources != nil) return backup, nil } @@ -328,6 +576,37 @@ func (r *RestoreJobReconciler) reconcileVeleroRestore(ctx context.Context, resto logger := getLogger(ctx) logger.Debug("reconciling Velero strategy restore", "restorejob", restoreJob.Name, "backup", backup.Name) + // Parse restore options from the opaque blob + restoreOpts, err := parseRestoreOptions(restoreJob.Spec.Options) + if err != nil { + return r.markRestoreJobFailed(ctx, restoreJob, fmt.Sprintf("invalid restore options: %v", err)) + } + + target := resolveRestoreTarget(restoreJob, backup, restoreOpts) + logger.Debug("resolved restore target", "targetNS", target.Namespace, "targetApp", target.AppName, "isCopy", target.IsCopy) + + // Validate: target namespace must exist for cross-namespace copies + if target.IsCopy { + targetNS := &corev1.Namespace{} + if err := r.Get(ctx, client.ObjectKey{Name: target.Namespace}, targetNS); err != nil { + if errors.IsNotFound(err) { + return r.markRestoreJobFailed(ctx, restoreJob, fmt.Sprintf( + "target namespace %q does not exist; create it before requesting a cross-namespace restore", + target.Namespace)) + } + return ctrl.Result{}, fmt.Errorf("failed to check target namespace %q: %w", target.Namespace, err) + } + } + + // Validate: same-namespace restore with a different app name is not supported + // due to Velero DataUpload always writing to PVCs with the original name. + if !target.IsCopy && target.AppName != backup.Spec.ApplicationRef.Name { + return r.markRestoreJobFailed(ctx, restoreJob, + "restoring to the same namespace with a different application name is not supported "+ + "due to Velero DataUpload limitations: data is always uploaded to PVCs with the original name. "+ + "Use options.targetNamespace to restore into a different namespace") + } + // Step 1: On first reconcile, set startedAt and phase = Running if restoreJob.Status.StartedAt == nil { logger.Debug("setting RestoreJob StartedAt and phase to Running") @@ -378,9 +657,40 @@ func (r *RestoreJobReconciler) reconcileVeleroRestore(ctx context.Context, resto } if len(veleroRestoreList.Items) == 0 { + // For copy restores, enforce failIfTargetExists before touching anything. + // In-place restores are excluded: the source application is expected to exist + // and will be halted/overwritten deliberately. + if target.IsCopy && restoreOpts.GetFailIfTargetExists() { + targetHRName := helmReleaseNameForApp(target.AppKind, target.AppName) + exists, err := r.targetHelmReleaseExists(ctx, target.AppKind, targetHRName, target.Namespace) + if err != nil { + logger.Error(err, "failed to check whether target HelmRelease exists") + return ctrl.Result{}, err + } + if exists { + return r.markRestoreJobFailed(ctx, restoreJob, fmt.Sprintf( + "target application %q already exists in namespace %q; "+ + "set options.failIfTargetExists=false to overwrite", + target.AppName, target.Namespace)) + } + } + + // Resolve underlying resources once; prefer Backup status, fall back to Velero annotation. + ur := r.resolveUnderlyingResourcesForRestore(ctx, backup, veleroBackupName) + + // Pre-restore: graceful shutdown, suspend HRs, rename PVCs (skipped for copy) + ready, result, err := r.prepareForRestore(ctx, restoreJob, backup, ur, target, restoreOpts) + if err != nil { + return r.markRestoreJobFailed(ctx, restoreJob, fmt.Sprintf("pre-restore preparation failed: %v", err)) + } + if !ready { + logger.Debug("pre-restore preparation in progress, requeuing") + return result, nil + } + // Create Velero Restore logger.Debug("Velero Restore not found, creating new one") - if err := r.createVeleroRestore(ctx, restoreJob, backup, veleroStrategy, veleroBackupName); err != nil { + if err := r.createVeleroRestore(ctx, restoreJob, backup, veleroStrategy, veleroBackupName, ur, target, restoreOpts); err != nil { logger.Error(err, "failed to create Velero Restore") return r.markRestoreJobFailed(ctx, restoreJob, fmt.Sprintf("failed to create Velero Restore: %v", err)) } @@ -406,6 +716,18 @@ func (r *RestoreJobReconciler) reconcileVeleroRestore(ctx context.Context, resto // Step 4: On success if phase == "Completed" { + // Post-restore: rename resources if target app name differs from source. + // Velero resource modifiers cannot change metadata.name, so we do it after restore. + if target.IsRenamed { + if err := r.postRestoreRename(ctx, restoreJob, backup, target); err != nil { + r.cleanupResourceModifierConfigMaps(ctx, restoreJob) + return r.markRestoreJobFailed(ctx, restoreJob, fmt.Sprintf("post-restore rename failed: %v", err)) + } + } + + // Clean up resource modifier ConfigMaps now that the restore is complete. + r.cleanupResourceModifierConfigMaps(ctx, restoreJob) + now := metav1.Now() restoreJob.Status.CompletedAt = &now restoreJob.Status.Phase = backupsv1alpha1.RestoreJobPhaseSucceeded @@ -419,6 +741,7 @@ func (r *RestoreJobReconciler) reconcileVeleroRestore(ctx context.Context, resto // Step 5: On failure if phase == "Failed" || phase == "PartiallyFailed" { + r.cleanupResourceModifierConfigMaps(ctx, restoreJob) message := fmt.Sprintf("Velero Restore failed with phase: %s", phase) if veleroRestore.Status.FailureReason != "" { message = fmt.Sprintf("%s: %s", message, veleroRestore.Status.FailureReason) @@ -430,31 +753,593 @@ func (r *RestoreJobReconciler) reconcileVeleroRestore(ctx context.Context, resto return ctrl.Result{RequeueAfter: defaultRestoreRequeueAfter}, nil } -// createVeleroRestore creates a Velero Restore resource. -func (r *RestoreJobReconciler) createVeleroRestore(ctx context.Context, restoreJob *backupsv1alpha1.RestoreJob, backup *backupsv1alpha1.Backup, strategy *strategyv1alpha1.Velero, veleroBackupName string) error { +// Velero resource modifier types (local mirrors of the internal Velero types). + +type resourceModifiers struct { + Version string `yaml:"version"` + ResourceModifierRules []resourceModifierRule `yaml:"resourceModifierRules"` +} + +type resourceModifierRule struct { + Conditions resourceModifierConditions `yaml:"conditions"` + MergePatches []mergePatch `yaml:"mergePatches,omitempty"` + Patches []jsonPatch `yaml:"patches,omitempty"` +} + +type resourceModifierConditions struct { + GroupResource string `yaml:"groupResource"` + ResourceNameRegex string `yaml:"resourceNameRegex,omitempty"` + Namespaces []string `yaml:"namespaces,omitempty"` +} + +type mergePatch struct { + PatchData string `yaml:"patchData"` +} + +type jsonPatch struct { + Operation string `yaml:"operation"` + Path string `yaml:"path"` + Value string `yaml:"value,omitempty"` +} + +// marshalPatchData marshals an arbitrary object to YAML for use as +// mergePatch.PatchData in Velero resource modifiers. +func marshalPatchData(v interface{}) (string, error) { + b, err := yaml.Marshal(v) + if err != nil { + return "", fmt.Errorf("failed to marshal patch data: %w", err) + } + return string(b), nil +} + +// createResourceModifiersConfigMap creates a Velero resource modifiers ConfigMap +// that patches VM resources during restore: +// - PVC adoption: always adds cdi.kubevirt.io/allowClaimAdoption=true to all +// restored PVCs so CDI can adopt them when a HelmRelease of VMDisk recreates a DV. +// - OVN IP/MAC: sets OVN annotations on the VirtualMachine for correct ssh access to restored VM. +func (r *RestoreJobReconciler) createResourceModifiersConfigMap(ctx context.Context, restoreJob *backupsv1alpha1.RestoreJob, backup *backupsv1alpha1.Backup, ur *runtime.RawExtension, target restoreTarget, opts RestoreOptions) (*corev1.ConfigMap, error) { logger := getLogger(ctx) - logger.Debug("createVeleroRestore called", "strategy", strategy.Name, "veleroBackupName", veleroBackupName) - // Determine target application reference - targetAppRef := r.getTargetApplicationRef(restoreJob, backup) + targetNS := target.Namespace - // Get the target application object for templating - mapping, err := r.RESTMapping(schema.GroupKind{Group: *targetAppRef.APIGroup, Kind: targetAppRef.Kind}) + var rules []resourceModifierRule + + // PVC adoption: allow CDI to adopt restored PVCs when the HelmRelease recreates a DV. + pvcPatch, err := marshalPatchData(map[string]interface{}{ + "metadata": map[string]interface{}{ + "annotations": map[string]string{ + cdiAllowClaimAdoption: "true", + }, + }, + }) if err != nil { - return fmt.Errorf("failed to get REST mapping for target application: %w", err) + return nil, err } + rules = append(rules, resourceModifierRule{ + Conditions: resourceModifierConditions{ + GroupResource: "persistentvolumeclaims", + ResourceNameRegex: ".*", + Namespaces: []string{targetNS}, + }, + MergePatches: []mergePatch{{PatchData: pvcPatch}}, + }) + + // For cross-namespace restore: strip Velero's dynamic PV restore selector and + // volumeName from PVCs so the storage provisioner can dynamically provision new PVs. + // Without this, Velero's CSI PVCAction adds spec.selector with a velero.io/dynamic-pv-restore + // label that prevents dynamic provisioning when PVs are not included in the restore. + // Uses merge patch (null values) instead of JSON Patch remove to avoid RFC 6902 + // failures when the fields don't exist on the PVC (e.g. statically provisioned PVCs). + if target.IsCopy { + pvcStripPatch, err := marshalPatchData(map[string]interface{}{ + "spec": map[string]interface{}{ + "selector": nil, + "volumeName": nil, + }, + }) + if err != nil { + return nil, err + } + rules = append(rules, resourceModifierRule{ + Conditions: resourceModifierConditions{ + GroupResource: "persistentvolumeclaims", + ResourceNameRegex: ".*", + Namespaces: []string{targetNS}, + }, + MergePatches: []mergePatch{{PatchData: pvcStripPatch}}, + }) + } + + // OVN IP/MAC annotations on VirtualMachine for correct network identity after restore. + // Only applied when keepOriginalIpAndMac is true; for restore-to-copy the copy + // should get new IP/MAC from the network to avoid conflicts. + if opts.GetKeepOriginalIpAndMac() { + if vmRes := getVMInstanceResources(ur); vmRes != nil && (vmRes.IP != "" || vmRes.MAC != "") { + ovnAnnotations := map[string]string{} + if vmRes.IP != "" { + ovnAnnotations[ovnIPAnnotation] = vmRes.IP + } + if vmRes.MAC != "" { + ovnAnnotations[ovnMACAnnotation] = vmRes.MAC + } + vmPatch, err := marshalPatchData(map[string]interface{}{ + "spec": map[string]interface{}{ + "template": map[string]interface{}{ + "metadata": map[string]interface{}{ + "annotations": ovnAnnotations, + }, + }, + }, + }) + if err != nil { + return nil, err + } + rules = append(rules, resourceModifierRule{ + Conditions: resourceModifierConditions{ + GroupResource: "virtualmachines.kubevirt.io", + ResourceNameRegex: ".*", + Namespaces: []string{targetNS}, + }, + MergePatches: []mergePatch{{PatchData: vmPatch}}, + }) + } + } + + rulesYAML, err := yaml.Marshal(resourceModifiers{ + Version: "v1", + ResourceModifierRules: rules, + }) + if err != nil { + return nil, fmt.Errorf("failed to marshal resource modifier rules: %w", err) + } + + cmName := fmt.Sprintf("restore-modifiers-%s-%s", restoreJob.Namespace, restoreJob.Name) + // Truncate name to fit Kubernetes 253-char limit + if len(cmName) > 253 { + cmName = cmName[:253] + } + + cm := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: cmName, + Namespace: veleroNamespace, + Labels: map[string]string{ + backupsv1alpha1.OwningJobNameLabel: restoreJob.Name, + backupsv1alpha1.OwningJobNamespaceLabel: restoreJob.Namespace, + }, + }, + Data: map[string]string{ + "resource-modifier-rules.yaml": string(rulesYAML), + }, + } + + if err := r.Create(ctx, cm); err != nil { + if errors.IsAlreadyExists(err) { + // ConfigMap already exists (e.g. RestoreJob recreated with same name). + // Update its data to reflect the current backup's underlying resources. + existing := &corev1.ConfigMap{} + if err := r.Get(ctx, client.ObjectKey{Namespace: veleroNamespace, Name: cmName}, existing); err != nil { + return nil, fmt.Errorf("failed to get existing resourceModifiers ConfigMap: %w", err) + } + existing.Data = cm.Data + if err := r.Update(ctx, existing); err != nil { + return nil, fmt.Errorf("failed to update existing resourceModifiers ConfigMap: %w", err) + } + logger.Debug("updated existing resourceModifiers ConfigMap", "name", cmName) + return existing, nil + } + return nil, fmt.Errorf("failed to create resourceModifiers ConfigMap: %w", err) + } + + logger.Debug("created resourceModifiers ConfigMap", "name", cm.Name, "namespace", cm.Namespace) + return cm, nil +} + +// resolveUnderlyingResourcesForRestore returns underlying resources for symmetric +// restore label selectors. Velero Backup annotation is used when Backup.status was empty +// (e.g. CRD without underlyingResources in schema). +func (r *RestoreJobReconciler) resolveUnderlyingResourcesForRestore(ctx context.Context, backup *backupsv1alpha1.Backup, veleroBackupName string) *runtime.RawExtension { + if backup.Status.UnderlyingResources != nil && len(backup.Status.UnderlyingResources.Raw) > 0 { + return backup.Status.UnderlyingResources + } + vb := &velerov1.Backup{} + if err := r.Get(ctx, client.ObjectKey{Namespace: veleroNamespace, Name: veleroBackupName}, vb); err != nil { + return backup.Status.UnderlyingResources + } + if urJSON, ok := vb.Annotations[underlyingResourcesAnnotation]; ok && urJSON != "" { + return &runtime.RawExtension{Raw: []byte(urJSON)} + } + return backup.Status.UnderlyingResources +} + +// GVRs used during pre-restore preparation. +var ( + helmReleaseGVR = schema.GroupVersionResource{Group: "helm.toolkit.fluxcd.io", Version: "v2", Resource: "helmreleases"} + virtualMachineGVR = schema.GroupVersionResource{Group: "kubevirt.io", Version: "v1", Resource: "virtualmachines"} + vmiGVR = schema.GroupVersionResource{Group: "kubevirt.io", Version: "v1", Resource: "virtualmachineinstances"} + dataVolumeGVR = schema.GroupVersionResource{Group: "cdi.kubevirt.io", Version: "v1beta1", Resource: "datavolumes"} +) + +// helmReleaseNameForApp returns the HelmRelease name for the given application +// kind and name. Returns empty string for unsupported kinds. +func helmReleaseNameForApp(appKind, appName string) string { + switch appKind { + case vmInstanceKind: + return vmNamePrefix + appName + case vmDiskAppKind: + return vmDiskNamePrefix + appName + default: + return "" + } +} + +// targetHelmReleaseExists returns true when a HelmRelease with the given name +// already exists in namespace. Returns false for unsupported application kinds +// (those where helmReleaseNameForApp returns ""). +func (r *RestoreJobReconciler) targetHelmReleaseExists(ctx context.Context, appKind, hrName, namespace string) (bool, error) { + if hrName == "" { + return false, nil + } + _, err := r.Resource(helmReleaseGVR).Namespace(namespace).Get(ctx, hrName, metav1.GetOptions{}) + if err != nil { + if errors.IsNotFound(err) { + return false, nil + } + return false, err + } + return true, nil +} + +// shortHash returns the first 4 hex characters of sha256(input). +func shortHash(input string) string { + h := sha256.Sum256([]byte(input)) + return hex.EncodeToString(h[:])[:4] +} + +// prepareForRestore performs graceful pre-restore cleanup: +// 1. Suspends HelmReleases that belong to the backup scope. +// 2. Halts the VirtualMachine (sets spec.runStrategy=Halted). +// 3. Waits for the VMI to disappear (graceful shutdown complete). +// 4. Deletes DataVolumes so CDI doesn't recreate PVCs after rename. +// 5. Renames existing PVCs to -orig- so Velero can create fresh +// ones via Data Movement. +// +// Failures in individual steps are non-fatal: missing resources are expected +// (e.g. restore requested when app was already deleted). Each action emits +// a Kubernetes Event on the RestoreJob for observability. +// +// postRestoreRename renames VMInstance HelmRelease after Velero Restore completes. +// Velero resource modifiers cannot change metadata.name, so this step creates +// a new HelmRelease with the target name and deletes the old one. +// Flux will reconcile the renamed HelmRelease and recreate downstream resources +// (VM, VMI) with the new name. +func (r *RestoreJobReconciler) postRestoreRename(ctx context.Context, restoreJob *backupsv1alpha1.RestoreJob, backup *backupsv1alpha1.Backup, target restoreTarget) error { + logger := getLogger(ctx) + sourceAppName := backup.Spec.ApplicationRef.Name + sourceHRName := vmNamePrefix + sourceAppName + targetHRName := vmNamePrefix + target.AppName + + logger.Debug("post-restore rename", "from", sourceHRName, "to", targetHRName, "namespace", target.Namespace) + + // Get the restored HelmRelease with the original name + hrClient := r.Resource(helmReleaseGVR).Namespace(target.Namespace) + oldHR, err := hrClient.Get(ctx, sourceHRName, metav1.GetOptions{}) + if err != nil { + if errors.IsNotFound(err) { + logger.Debug("source HelmRelease not found, skipping rename", "name", sourceHRName) + return nil + } + return fmt.Errorf("failed to get HelmRelease %s: %w", sourceHRName, err) + } + + // Create new HelmRelease with the target name + newHR := oldHR.DeepCopy() + newHR.SetName(targetHRName) + newHR.SetResourceVersion("") + newHR.SetUID("") + newHR.SetCreationTimestamp(metav1.Time{}) + newHR.SetManagedFields(nil) + newHR.SetGeneration(0) + + // Update labels + labels := newHR.GetLabels() + if labels == nil { + labels = map[string]string{} + } + labels[appNameLabel] = target.AppName + labels["app.kubernetes.io/instance"] = targetHRName + labels["helm.toolkit.fluxcd.io/name"] = targetHRName + newHR.SetLabels(labels) + + // Remove Velero restore annotations/labels that tie it to the old restore + annotations := newHR.GetAnnotations() + delete(annotations, "velero.io/restore-name") + newHR.SetAnnotations(annotations) + + // Clear status so Flux reconciles fresh + unstructured.RemoveNestedField(newHR.Object, "status") + + if _, err := hrClient.Create(ctx, newHR, metav1.CreateOptions{}); err != nil { + if errors.IsAlreadyExists(err) { + logger.Debug("target HelmRelease already exists, skipping create", "name", targetHRName) + } else { + return fmt.Errorf("failed to create renamed HelmRelease %s: %w", targetHRName, err) + } + } else { + r.Recorder.Event(restoreJob, corev1.EventTypeNormal, "PostRestoreRename", + fmt.Sprintf("Created renamed HelmRelease %s (from %s)", targetHRName, sourceHRName)) + } + + // Delete the old HelmRelease + if err := hrClient.Delete(ctx, sourceHRName, metav1.DeleteOptions{}); err != nil && !errors.IsNotFound(err) { + return fmt.Errorf("failed to delete old HelmRelease %s: %w", sourceHRName, err) + } + r.Recorder.Event(restoreJob, corev1.EventTypeNormal, "PostRestoreRename", + fmt.Sprintf("Deleted old HelmRelease %s", sourceHRName)) + + logger.Debug("post-restore rename complete", "from", sourceHRName, "to", targetHRName) + return nil +} + +// Returns true when preparation is complete and the Velero Restore can be created. +// Returns false (with a requeue) when still waiting for VM shutdown. +func (r *RestoreJobReconciler) prepareForRestore(ctx context.Context, restoreJob *backupsv1alpha1.RestoreJob, backup *backupsv1alpha1.Backup, ur *runtime.RawExtension, target restoreTarget, opts RestoreOptions) (ready bool, result ctrl.Result, err error) { + // For restore-to-copy, skip all source-app preparation. + // The source application remains untouched; we're restoring a copy into another namespace. + if target.IsCopy { + r.Recorder.Event(restoreJob, corev1.EventTypeNormal, "PrepareForRestore", + "Restore to copy: skipping source application preparation") + return true, ctrl.Result{}, nil + } + ns := restoreJob.Namespace - if mapping.Scope.Name() != meta.RESTScopeNameNamespace { - ns = "" + appName := backup.Spec.ApplicationRef.Name + appKind := backup.Spec.ApplicationRef.Kind + origSuffix := "-orig-" + shortHash(restoreJob.Name) + + // --- Step 1: Suspend HelmReleases --- + vmRes := getVMInstanceResources(ur) + hrNames := []string{} + if appKind == vmInstanceKind { + hrNames = append(hrNames, vmNamePrefix+appName) } - app, err := r.Resource(mapping.Resource).Namespace(ns).Get(ctx, targetAppRef.Name, metav1.GetOptions{}) - if err != nil { - return fmt.Errorf("failed to get target application: %w", err) + if vmRes != nil { + for _, dv := range vmRes.DataVolumes { + hrNames = append(hrNames, dv.DataVolumeName) + } + } + for _, hrName := range hrNames { + if err := r.suspendHelmRelease(ctx, ns, hrName); err != nil { + r.Recorder.Event(restoreJob, corev1.EventTypeWarning, "PrepareForRestore", + fmt.Sprintf("Failed to suspend HelmRelease %s: %v", hrName, err)) + } else { + r.Recorder.Event(restoreJob, corev1.EventTypeNormal, "PrepareForRestore", + fmt.Sprintf("Suspended HelmRelease %s", hrName)) + } } - // Build template context + // --- Step 2: Halt VM and wait for shutdown --- + if appKind == vmInstanceKind { + vmName := vmNamePrefix + appName + halted, err := r.haltVirtualMachine(ctx, ns, vmName) + if err != nil { + r.Recorder.Event(restoreJob, corev1.EventTypeWarning, "PrepareForRestore", + fmt.Sprintf("Failed to halt VM %s: %v", vmName, err)) + // Non-fatal: proceed even if halting fails (VM might not exist) + } else if !halted { + r.Recorder.Event(restoreJob, corev1.EventTypeNormal, "PrepareForRestore", + fmt.Sprintf("Waiting for VM %s to shut down", vmName)) + return false, ctrl.Result{RequeueAfter: defaultRestoreRequeueAfter}, nil + } else { + r.Recorder.Event(restoreJob, corev1.EventTypeNormal, "PrepareForRestore", + fmt.Sprintf("VM %s is halted", vmName)) + } + } + + // --- Step 3: Rename PVCs to -orig- --- + // Must happen BEFORE deleting DVs: the PVC has an ownerReference to the DV, + // so deleting the DV first would cascade-delete the PVC via garbage collection. + // Only when keepOriginalPVC is true (default for in-place restore). + if opts.GetKeepOriginalPVC() && vmRes != nil { + for _, dv := range vmRes.DataVolumes { + if err := r.renamePVC(ctx, restoreJob, ns, dv.DataVolumeName, dv.DataVolumeName+origSuffix); err != nil { + r.Recorder.Event(restoreJob, corev1.EventTypeWarning, "PrepareForRestore", + fmt.Sprintf("Failed to keep old PVC %s: %v", dv.DataVolumeName, err)) + } + } + } + + // --- Step 4: Delete DataVolumes so CDI doesn't recreate PVCs --- + if vmRes != nil { + for _, dv := range vmRes.DataVolumes { + if err := r.deleteDataVolume(ctx, ns, dv.DataVolumeName); err != nil { + r.Recorder.Event(restoreJob, corev1.EventTypeWarning, "PrepareForRestore", + fmt.Sprintf("Failed to delete DataVolume %s: %v", dv.DataVolumeName, err)) + } else { + r.Recorder.Event(restoreJob, corev1.EventTypeNormal, "PrepareForRestore", + fmt.Sprintf("Deleted DataVolume %s", dv.DataVolumeName)) + } + } + } + + r.Recorder.Event(restoreJob, corev1.EventTypeNormal, "PrepareForRestore", "Pre-restore preparation complete") + return true, ctrl.Result{}, nil +} + +// suspendHelmRelease sets spec.suspend=true on a HelmRelease. +func (r *RestoreJobReconciler) suspendHelmRelease(ctx context.Context, ns, name string) error { + hr, err := r.Resource(helmReleaseGVR).Namespace(ns).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + if errors.IsNotFound(err) { + return nil + } + return err + } + suspended, _, _ := unstructured.NestedBool(hr.Object, "spec", "suspend") + if suspended { + return nil + } + if err := unstructured.SetNestedField(hr.Object, true, "spec", "suspend"); err != nil { + return err + } + if _, err := r.Resource(helmReleaseGVR).Namespace(ns).Update(ctx, hr, metav1.UpdateOptions{}); err != nil { + return err + } + return nil +} + +// haltVirtualMachine sets runStrategy=Halted and returns true when the VMI is gone. +func (r *RestoreJobReconciler) haltVirtualMachine(ctx context.Context, ns, vmName string) (bool, error) { + vm, err := r.Resource(virtualMachineGVR).Namespace(ns).Get(ctx, vmName, metav1.GetOptions{}) + if err != nil { + if errors.IsNotFound(err) { + return true, nil + } + return false, err + } + + currentStrategy, _, _ := unstructured.NestedString(vm.Object, "spec", "runStrategy") + if currentStrategy != "Halted" { + if err := unstructured.SetNestedField(vm.Object, "Halted", "spec", "runStrategy"); err != nil { + return false, err + } + if _, err := r.Resource(virtualMachineGVR).Namespace(ns).Update(ctx, vm, metav1.UpdateOptions{}); err != nil { + return false, err + } + } + + // VMI gone = shutdown complete + _, err = r.Resource(vmiGVR).Namespace(ns).Get(ctx, vmName, metav1.GetOptions{}) + if err != nil { + if errors.IsNotFound(err) { + return true, nil + } + return false, err + } + return false, nil +} + +// deleteDataVolume deletes a DataVolume so CDI doesn't recreate the PVC after rename. +// Uses Orphan propagation to avoid cascade-deleting the PVC that the DV owns via +// ownerReference. Without this, keepOriginalPVC=false would silently destroy the +// original PVC through garbage collection instead of leaving it for Velero to overwrite. +func (r *RestoreJobReconciler) deleteDataVolume(ctx context.Context, ns, name string) error { + orphan := metav1.DeletePropagationOrphan + err := r.Resource(dataVolumeGVR).Namespace(ns).Delete(ctx, name, metav1.DeleteOptions{ + PropagationPolicy: &orphan, + }) + if err != nil && !errors.IsNotFound(err) { + return err + } + return nil +} + +// renamePVC preserves an existing PVC by rebinding it under a new name. +// The original PVC is deleted and a new one pointing to the same PV is created. +// Missing resources are silently skipped (non-fatal). +func (r *RestoreJobReconciler) renamePVC(ctx context.Context, restoreJob *backupsv1alpha1.RestoreJob, ns, oldName, newName string) error { + logger := getLogger(ctx) + + // Check if already renamed + existingNew := &corev1.PersistentVolumeClaim{} + if err := r.Get(ctx, client.ObjectKey{Namespace: ns, Name: newName}, existingNew); err == nil { + return nil + } + + // Get the original PVC + oldPVC := &corev1.PersistentVolumeClaim{} + if err := r.Get(ctx, client.ObjectKey{Namespace: ns, Name: oldName}, oldPVC); err != nil { + if errors.IsNotFound(err) { + return nil // nothing to rename + } + return err + } + + pvName := oldPVC.Spec.VolumeName + if pvName == "" { + logger.Debug("PVC not bound, deleting", "name", oldName) + return r.Delete(ctx, oldPVC) + } + + // Patch PV reclaim policy to Retain so it survives PVC deletion + pv := &corev1.PersistentVolume{} + if err := r.Get(ctx, client.ObjectKey{Name: pvName}, pv); err != nil { + return fmt.Errorf("failed to get PV %s: %w", pvName, err) + } + if pv.Spec.PersistentVolumeReclaimPolicy != corev1.PersistentVolumeReclaimRetain { + pv.Spec.PersistentVolumeReclaimPolicy = corev1.PersistentVolumeReclaimRetain + if err := r.Update(ctx, pv); err != nil { + return fmt.Errorf("failed to set Retain policy on PV %s: %w", pvName, err) + } + } + + // Create the new -orig PVC first (unbound, just the object) + newPVC := &corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: newName, + Namespace: ns, + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: oldPVC.Spec.AccessModes, + Resources: oldPVC.Spec.Resources, + StorageClassName: oldPVC.Spec.StorageClassName, + VolumeMode: oldPVC.Spec.VolumeMode, + VolumeName: pvName, + }, + } + if err := r.Create(ctx, newPVC); err != nil && !errors.IsAlreadyExists(err) { + return fmt.Errorf("failed to create -orig PVC %s: %w", newName, err) + } + // Re-read to get UID for the claimRef + if err := r.Get(ctx, client.ObjectKey{Namespace: ns, Name: newName}, newPVC); err != nil { + return fmt.Errorf("failed to get -orig PVC %s: %w", newName, err) + } + + // Delete the original PVC + if err := r.Delete(ctx, oldPVC); err != nil && !errors.IsNotFound(err) { + return fmt.Errorf("failed to delete PVC %s: %w", oldName, err) + } + + // Point the PV's claimRef directly to the new -orig PVC. + // This is atomic — no window where the PV is Available for other PVCs to grab. + if err := r.Get(ctx, client.ObjectKey{Name: pvName}, pv); err != nil { + return fmt.Errorf("failed to re-fetch PV %s: %w", pvName, err) + } + pv.Spec.ClaimRef = &corev1.ObjectReference{ + APIVersion: "v1", + Kind: "PersistentVolumeClaim", + Namespace: ns, + Name: newName, + UID: newPVC.UID, + } + if err := r.Update(ctx, pv); err != nil { + return fmt.Errorf("failed to rebind PV %s to %s: %w", pvName, newName, err) + } + + r.Recorder.Event(restoreJob, corev1.EventTypeNormal, "PrepareForRestore", + fmt.Sprintf("Keep old PVC %s as %s (PV: %s)", oldName, newName, pvName)) + return nil +} + +// createVeleroRestore creates a Velero Restore resource. +func (r *RestoreJobReconciler) createVeleroRestore(ctx context.Context, restoreJob *backupsv1alpha1.RestoreJob, backup *backupsv1alpha1.Backup, strategy *strategyv1alpha1.Velero, veleroBackupName string, ur *runtime.RawExtension, target restoreTarget, opts RestoreOptions) error { + logger := getLogger(ctx) + logger.Debug("createVeleroRestore called", "strategy", strategy.Name, "veleroBackupName", veleroBackupName, "targetNS", target.Namespace, "isCopy", target.IsCopy) + + // For restore template context, always use the source (backup) namespace and app name. + // The strategy template uses includedNamespaces and orLabelSelectors to select + // resources from the backup tarball, which are stored under the source namespace + // and labeled with the source app name. + // Velero's namespaceMapping handles redirecting to the target namespace; + // resource modifiers handle renaming when the target app name differs. templateContext := map[string]interface{}{ - "Application": app.Object, + "Application": map[string]interface{}{ + "metadata": map[string]interface{}{ + "name": backup.Spec.ApplicationRef.Name, + "namespace": backup.Namespace, + }, + "kind": backup.Spec.ApplicationRef.Kind, + }, // TODO: Parameters are not currently stored on Backup, so they're unavailable during restore. // This is a design limitation that should be addressed by persisting Parameters on the Backup object. "Parameters": map[string]string{}, @@ -473,6 +1358,46 @@ func (r *RestoreJobReconciler) createVeleroRestore(ctx context.Context, restoreJ // Set the backupName in the spec (required by Velero) veleroRestoreSpec.BackupName = veleroBackupName + // For restore-to-copy, set Velero namespaceMapping to redirect resources + // from the source namespace to the target namespace. + if target.IsCopy { + if veleroRestoreSpec.NamespaceMapping == nil { + veleroRestoreSpec.NamespaceMapping = make(map[string]string) + } + veleroRestoreSpec.NamespaceMapping[backup.Namespace] = target.Namespace + logger.Debug("set namespaceMapping on Velero Restore", "from", backup.Namespace, "to", target.Namespace) + } + + // Match backup: add OR selectors for each underlying VMDisk so restore applies the same + // scope as the intended backup (see createVeleroBackup). + if vmRes := getVMInstanceResources(ur); vmRes != nil { + for _, dv := range vmRes.DataVolumes { + veleroRestoreSpec.OrLabelSelectors = append(veleroRestoreSpec.OrLabelSelectors, &metav1.LabelSelector{ + MatchLabels: map[string]string{ + appKindLabel: vmDiskAppKind, + appNameLabel: dv.ApplicationName, + }, + }) + } + if len(vmRes.DataVolumes) > 0 { + logger.Debug("added VMDisk label selectors to Velero restore", "count", len(vmRes.DataVolumes)) + } + } + + // Create resourceModifiers ConfigMap + resourceModifierCM, err := r.createResourceModifiersConfigMap(ctx, restoreJob, backup, ur, target, opts) + if err != nil { + return fmt.Errorf("failed to create resourceModifiers ConfigMap: %w", err) + } + if resourceModifierCM != nil { + veleroRestoreSpec.ResourceModifier = &corev1.TypedLocalObjectReference{ + APIGroup: stringPtr(""), + Kind: "ConfigMap", + Name: resourceModifierCM.Name, + } + logger.Debug("set resourceModifier on Velero Restore", "configMap", resourceModifierCM.Name) + } + generateName := fmt.Sprintf("%s.%s-", restoreJob.Namespace, restoreJob.Name) veleroRestore := &velerov1.Restore{ ObjectMeta: metav1.ObjectMeta{ @@ -497,3 +1422,60 @@ func (r *RestoreJobReconciler) createVeleroRestore(ctx context.Context, restoreJ fmt.Sprintf("Created Velero Restore %s/%s", veleroNamespace, veleroRestore.Name)) return nil } + +// dataUploadListGVK is the API version shipped with Velero data mover CRDs (see velero datauploads CRD). +var dataUploadListGVK = schema.GroupVersionKind{Group: "velero.io", Version: "v2alpha1", Kind: "DataUploadList"} + +// formatVeleroBackupFailureMessageForBackupJob builds a BackupJob status message from Velero Backup +// status plus failed DataUpload resources (CSI data mover), similar to `velero backup describe`. +func formatVeleroBackupFailureMessageForBackupJob(ctx context.Context, c client.Client, veleroBackup *velerov1.Backup) string { + var b strings.Builder + fmt.Fprintf(&b, "Velero Backup failed with phase %s", veleroBackup.Status.Phase) + if fr := strings.TrimSpace(veleroBackup.Status.FailureReason); fr != "" { + fmt.Fprintf(&b, ": %s", fr) + } + if len(veleroBackup.Status.ValidationErrors) > 0 { + fmt.Fprintf(&b, "; validation: %v", veleroBackup.Status.ValidationErrors) + } + if h := veleroBackup.Status.HookStatus; h != nil && h.HooksFailed > 0 { + fmt.Fprintf(&b, "; hooks failed %d/%d", h.HooksFailed, h.HooksAttempted) + } + if veleroBackup.Status.BackupItemOperationsFailed > 0 { + fmt.Fprintf(&b, "; async item operations failed %d (completed %d, attempted %d)", + veleroBackup.Status.BackupItemOperationsFailed, + veleroBackup.Status.BackupItemOperationsCompleted, + veleroBackup.Status.BackupItemOperationsAttempted) + } + b.WriteString(appendFailedDataUploadMessages(ctx, c, veleroBackup.Name)) + return b.String() +} + +func appendFailedDataUploadMessages(ctx context.Context, c client.Client, veleroBackupName string) string { + ul := unstructured.UnstructuredList{} + ul.SetGroupVersionKind(dataUploadListGVK) + if err := c.List(ctx, &ul, client.InNamespace(veleroNamespace)); err != nil { + return "" + } + prefix := veleroBackupName + "-" + var b strings.Builder + for _, item := range ul.Items { + if !strings.HasPrefix(item.GetName(), prefix) { + continue + } + phase, _, _ := unstructured.NestedString(item.Object, "status", "phase") + if phase != "Failed" { + continue + } + msg, _, _ := unstructured.NestedString(item.Object, "status", "message") + if strings.TrimSpace(msg) == "" { + msg = "(empty status.message)" + } + srcPVC, _, _ := unstructured.NestedString(item.Object, "spec", "sourcePVC") + if srcPVC != "" { + fmt.Fprintf(&b, "; DataUpload %s failed for PVC %s: %s", item.GetName(), srcPVC, msg) + } else { + fmt.Fprintf(&b, "; DataUpload %s failed: %s", item.GetName(), msg) + } + } + return b.String() +} diff --git a/internal/backupcontroller/velerostrategy_controller_test.go b/internal/backupcontroller/velerostrategy_controller_test.go index a7de2076..7b085dbf 100644 --- a/internal/backupcontroller/velerostrategy_controller_test.go +++ b/internal/backupcontroller/velerostrategy_controller_test.go @@ -2,11 +2,13 @@ package backupcontroller import ( "context" + "encoding/json" "testing" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" dynamicfake "k8s.io/client-go/dynamic/fake" @@ -206,3 +208,1006 @@ func TestCreateVeleroBackup_TemplateContext(t *testing.T) { t.Errorf("Template context Parameters.backupStorageLocationName not applied correctly. Expected 'default-storage', got '%s'", veleroBackup.Spec.StorageLocation) } } + +func TestResolveRestoreTarget_NoTargetNamespace_InPlace(t *testing.T) { + restoreJob := &backupsv1alpha1.RestoreJob{ + ObjectMeta: metav1.ObjectMeta{ + Name: "restore-test", + Namespace: "tenant-root", + }, + Spec: backupsv1alpha1.RestoreJobSpec{ + BackupRef: corev1.LocalObjectReference{Name: "my-backup"}, + TargetApplicationRef: &corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("apps.cozystack.io"), + Kind: "VMInstance", + Name: "test-vm", + }, + }, + } + + backup := &backupsv1alpha1.Backup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-backup", + Namespace: "tenant-root", + }, + Spec: backupsv1alpha1.BackupSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("apps.cozystack.io"), + Kind: "VMInstance", + Name: "test-vm", + }, + }, + } + + // No targetNamespace in options → in-place restore + opts := RestoreOptions{} + target := resolveRestoreTarget(restoreJob, backup, opts) + + if target.IsCopy { + t.Error("expected IsCopy=false when targetNamespace is omitted, got true") + } + if target.Namespace != "tenant-root" { + t.Errorf("expected namespace 'tenant-root', got '%s'", target.Namespace) + } + if target.AppName != "test-vm" { + t.Errorf("expected appName 'test-vm', got '%s'", target.AppName) + } + if target.AppKind != "VMInstance" { + t.Errorf("expected appKind 'VMInstance', got '%s'", target.AppKind) + } +} + +func TestResolveRestoreTarget_SameTargetNamespace_InPlace(t *testing.T) { + restoreJob := &backupsv1alpha1.RestoreJob{ + ObjectMeta: metav1.ObjectMeta{ + Name: "restore-test", + Namespace: "tenant-root", + }, + Spec: backupsv1alpha1.RestoreJobSpec{ + BackupRef: corev1.LocalObjectReference{Name: "my-backup"}, + }, + } + + backup := &backupsv1alpha1.Backup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-backup", + Namespace: "tenant-root", + }, + Spec: backupsv1alpha1.BackupSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("apps.cozystack.io"), + Kind: "VMInstance", + Name: "test-vm", + }, + }, + } + + // targetNamespace equals backup namespace → still in-place + opts := RestoreOptions{ + CommonRestoreOptions: CommonRestoreOptions{ + TargetNamespace: "tenant-root", + }, + } + target := resolveRestoreTarget(restoreJob, backup, opts) + + if target.IsCopy { + t.Error("expected IsCopy=false when targetNamespace equals backup namespace, got true") + } + if target.Namespace != "tenant-root" { + t.Errorf("expected namespace 'tenant-root', got '%s'", target.Namespace) + } +} + +func TestResolveRestoreTarget_DifferentTargetNamespace_Copy(t *testing.T) { + restoreJob := &backupsv1alpha1.RestoreJob{ + ObjectMeta: metav1.ObjectMeta{ + Name: "restore-test", + Namespace: "tenant-root", + }, + Spec: backupsv1alpha1.RestoreJobSpec{ + BackupRef: corev1.LocalObjectReference{Name: "my-backup"}, + }, + } + + backup := &backupsv1alpha1.Backup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-backup", + Namespace: "tenant-root", + }, + Spec: backupsv1alpha1.BackupSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("apps.cozystack.io"), + Kind: "VMInstance", + Name: "test-vm", + }, + }, + } + + opts := RestoreOptions{ + CommonRestoreOptions: CommonRestoreOptions{ + TargetNamespace: "tenant-copy", + }, + } + target := resolveRestoreTarget(restoreJob, backup, opts) + + if !target.IsCopy { + t.Error("expected IsCopy=true when targetNamespace differs from backup namespace, got false") + } + if target.Namespace != "tenant-copy" { + t.Errorf("expected namespace 'tenant-copy', got '%s'", target.Namespace) + } +} + +// newTestRestoreJobReconcilerWithDynamic builds a RestoreJobReconciler with +// both static and dynamic fake clients. Use dynamicObjects to pre-populate +// unstructured resources (e.g. HelmReleases). +func newTestRestoreJobReconcilerWithDynamic(t *testing.T, dynamicObjects []runtime.Object, objects ...client.Object) *RestoreJobReconciler { + t.Helper() + testScheme := runtime.NewScheme() + _ = scheme.AddToScheme(testScheme) + _ = backupsv1alpha1.AddToScheme(testScheme) + _ = velerov1.AddToScheme(testScheme) + + fakeClient := clientfake.NewClientBuilder(). + WithScheme(testScheme). + WithObjects(objects...). + Build() + + dynamicClient := dynamicfake.NewSimpleDynamicClient(testScheme, dynamicObjects...) + + mapping := &meta.RESTMapping{ + Resource: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}, + GroupVersionKind: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"}, + Scope: meta.RESTScopeNamespace, + } + + return &RestoreJobReconciler{ + Client: fakeClient, + Interface: dynamicClient, + RESTMapper: &mockRESTMapper{mapping: mapping}, + Scheme: testScheme, + Recorder: record.NewFakeRecorder(100), + } +} + +// newTestRestoreJobReconciler builds a RestoreJobReconciler with fake clients for testing. +func newTestRestoreJobReconciler(t *testing.T, objects ...client.Object) *RestoreJobReconciler { + t.Helper() + testScheme := runtime.NewScheme() + _ = scheme.AddToScheme(testScheme) + _ = backupsv1alpha1.AddToScheme(testScheme) + _ = velerov1.AddToScheme(testScheme) + + fakeClient := clientfake.NewClientBuilder(). + WithScheme(testScheme). + WithObjects(objects...). + Build() + + dynamicClient := dynamicfake.NewSimpleDynamicClient(testScheme) + + mapping := &meta.RESTMapping{ + Resource: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}, + GroupVersionKind: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"}, + Scope: meta.RESTScopeNamespace, + } + + return &RestoreJobReconciler{ + Client: fakeClient, + Interface: dynamicClient, + RESTMapper: &mockRESTMapper{mapping: mapping}, + Scheme: testScheme, + Recorder: record.NewFakeRecorder(100), + } +} + +func TestPrepareForRestore_KeepOriginalPVCFalse_SkipsRename(t *testing.T) { + ns := "tenant-root" + + // Create a PVC that would be renamed if keepOriginalPVC were true + pvc := &corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vm-disk-ubuntu-source", + Namespace: ns, + }, + Spec: corev1.PersistentVolumeClaimSpec{}, + } + + restoreJob := &backupsv1alpha1.RestoreJob{ + ObjectMeta: metav1.ObjectMeta{ + Name: "restore-test", + Namespace: ns, + }, + Spec: backupsv1alpha1.RestoreJobSpec{ + BackupRef: corev1.LocalObjectReference{Name: "my-backup"}, + }, + } + + backup := &backupsv1alpha1.Backup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-backup", + Namespace: ns, + }, + Spec: backupsv1alpha1.BackupSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("apps.cozystack.io"), + Kind: "VMInstance", + Name: "test-vm", + }, + }, + } + + // underlyingResources with a DataVolume referencing the PVC + urData := vmInstanceResources{ + DataVolumes: []backupsv1alpha1.DataVolumeResource{ + {DataVolumeName: "vm-disk-ubuntu-source", ApplicationName: "ubuntu-source"}, + }, + } + urRaw, _ := json.Marshal(urData) + ur := &runtime.RawExtension{Raw: urRaw} + + target := restoreTarget{ + Namespace: ns, + AppName: "test-vm", + AppKind: "VMInstance", + IsCopy: false, + } + + // keepOriginalPVC = false → PVCs should NOT be renamed + opts := RestoreOptions{ + KeepOriginalPVC: boolPtr(false), + } + + reconciler := newTestRestoreJobReconciler(t, pvc, restoreJob, backup) + + ctx := context.Background() + ready, _, err := reconciler.prepareForRestore(ctx, restoreJob, backup, ur, target, opts) + if err != nil { + t.Fatalf("prepareForRestore() error = %v", err) + } + if !ready { + t.Fatal("expected ready=true, got false") + } + + // Verify the original PVC still exists with its original name (not renamed) + origPVC := &corev1.PersistentVolumeClaim{} + err = reconciler.Get(ctx, client.ObjectKey{Namespace: ns, Name: "vm-disk-ubuntu-source"}, origPVC) + if err != nil { + t.Errorf("original PVC should still exist with original name when keepOriginalPVC=false, got error: %v", err) + } + + // Verify no -orig PVC was created + origSuffix := "-orig-" + shortHash(restoreJob.Name) + renamedPVC := &corev1.PersistentVolumeClaim{} + err = reconciler.Get(ctx, client.ObjectKey{Namespace: ns, Name: "vm-disk-ubuntu-source" + origSuffix}, renamedPVC) + if err == nil { + t.Error("PVC should NOT have been renamed when keepOriginalPVC=false, but found renamed PVC") + } +} + +func TestResolveRestoreTarget_VMDisk_CommonRestoreOptions(t *testing.T) { + tests := []struct { + name string + targetNS string + wantIsCopy bool + wantNamespace string + }{ + { + name: "VMDisk in-place restore when targetNamespace is omitted", + targetNS: "", + wantIsCopy: false, + wantNamespace: "tenant-root", + }, + { + name: "VMDisk cross-namespace restore when targetNamespace differs", + targetNS: "tenant-copy", + wantIsCopy: true, + wantNamespace: "tenant-copy", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + restoreJob := &backupsv1alpha1.RestoreJob{ + ObjectMeta: metav1.ObjectMeta{ + Name: "restore-vmdisk", + Namespace: "tenant-root", + }, + Spec: backupsv1alpha1.RestoreJobSpec{ + BackupRef: corev1.LocalObjectReference{Name: "disk-backup"}, + TargetApplicationRef: &corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("apps.cozystack.io"), + Kind: "VMDisk", + Name: "ubuntu-source", + }, + }, + } + + backup := &backupsv1alpha1.Backup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "disk-backup", + Namespace: "tenant-root", + }, + Spec: backupsv1alpha1.BackupSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("apps.cozystack.io"), + Kind: "VMDisk", + Name: "ubuntu-source", + }, + }, + } + + // VMDisk uses only CommonRestoreOptions (no VMI-specific fields) + opts := RestoreOptions{ + CommonRestoreOptions: CommonRestoreOptions{ + TargetNamespace: tt.targetNS, + }, + } + + target := resolveRestoreTarget(restoreJob, backup, opts) + + if target.IsCopy != tt.wantIsCopy { + t.Errorf("IsCopy = %v, want %v", target.IsCopy, tt.wantIsCopy) + } + if target.Namespace != tt.wantNamespace { + t.Errorf("Namespace = %q, want %q", target.Namespace, tt.wantNamespace) + } + if target.AppKind != "VMDisk" { + t.Errorf("AppKind = %q, want 'VMDisk'", target.AppKind) + } + if target.AppName != "ubuntu-source" { + t.Errorf("AppName = %q, want 'ubuntu-source'", target.AppName) + } + }) + } +} + +func TestParseRestoreOptions_VMDiskOnlyCommonFields(t *testing.T) { + // Simulate a VMDisk restore where only CommonRestoreOptions fields are set + raw, _ := json.Marshal(map[string]interface{}{ + "targetNamespace": "tenant-copy", + "failIfTargetExists": true, + }) + ext := &runtime.RawExtension{Raw: raw} + + opts, err := parseRestoreOptions(ext) + if err != nil { + t.Fatalf("parseRestoreOptions() error = %v", err) + } + + if opts.TargetNamespace != "tenant-copy" { + t.Errorf("TargetNamespace = %q, want 'tenant-copy'", opts.TargetNamespace) + } + if !opts.GetFailIfTargetExists() { + t.Error("GetFailIfTargetExists() = false, want true") + } + // VMI-specific fields should be nil (not set by VMDisk options) + if opts.KeepOriginalPVC != nil { + t.Errorf("KeepOriginalPVC should be nil for VMDisk options, got %v", *opts.KeepOriginalPVC) + } + if opts.KeepOriginalIpAndMac != nil { + t.Errorf("KeepOriginalIpAndMac should be nil for VMDisk options, got %v", *opts.KeepOriginalIpAndMac) + } +} + +func TestRestoreOptions_Defaults(t *testing.T) { + t.Run("nil options default all bools to true", func(t *testing.T) { + opts, err := parseRestoreOptions(nil) + if err != nil { + t.Fatalf("parseRestoreOptions(nil) error = %v", err) + } + + if opts.TargetNamespace != "" { + t.Errorf("TargetNamespace default should be empty, got %q", opts.TargetNamespace) + } + if !opts.GetFailIfTargetExists() { + t.Error("GetFailIfTargetExists() should default to true") + } + if !opts.GetKeepOriginalPVC() { + t.Error("GetKeepOriginalPVC() should default to true") + } + if !opts.GetKeepOriginalIpAndMac() { + t.Error("GetKeepOriginalIpAndMac() should default to true") + } + }) + + t.Run("empty JSON defaults all bools to true", func(t *testing.T) { + raw, _ := json.Marshal(map[string]interface{}{}) + opts, err := parseRestoreOptions(&runtime.RawExtension{Raw: raw}) + if err != nil { + t.Fatalf("parseRestoreOptions({}) error = %v", err) + } + + if !opts.GetFailIfTargetExists() { + t.Error("GetFailIfTargetExists() should default to true") + } + if !opts.GetKeepOriginalPVC() { + t.Error("GetKeepOriginalPVC() should default to true") + } + if !opts.GetKeepOriginalIpAndMac() { + t.Error("GetKeepOriginalIpAndMac() should default to true") + } + }) + + t.Run("explicit false overrides defaults", func(t *testing.T) { + raw, _ := json.Marshal(map[string]interface{}{ + "failIfTargetExists": false, + "keepOriginalPVC": false, + "keepOriginalIpAndMac": false, + }) + opts, err := parseRestoreOptions(&runtime.RawExtension{Raw: raw}) + if err != nil { + t.Fatalf("parseRestoreOptions() error = %v", err) + } + + if opts.GetFailIfTargetExists() { + t.Error("GetFailIfTargetExists() should be false when explicitly set") + } + if opts.GetKeepOriginalPVC() { + t.Error("GetKeepOriginalPVC() should be false when explicitly set") + } + if opts.GetKeepOriginalIpAndMac() { + t.Error("GetKeepOriginalIpAndMac() should be false when explicitly set") + } + }) +} + +func TestResolveRestoreTarget_IsRenamed(t *testing.T) { + backup := &backupsv1alpha1.Backup{ + ObjectMeta: metav1.ObjectMeta{Name: "bk", Namespace: "tenant-root"}, + Spec: backupsv1alpha1.BackupSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("apps.cozystack.io"), + Kind: "VMInstance", + Name: "test-alpine", + }, + }, + } + + t.Run("same name is not renamed", func(t *testing.T) { + rj := &backupsv1alpha1.RestoreJob{ + ObjectMeta: metav1.ObjectMeta{Name: "rj", Namespace: "tenant-root"}, + Spec: backupsv1alpha1.RestoreJobSpec{ + BackupRef: corev1.LocalObjectReference{Name: "bk"}, + TargetApplicationRef: &corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("apps.cozystack.io"), + Kind: "VMInstance", + Name: "test-alpine", + }, + }, + } + target := resolveRestoreTarget(rj, backup, RestoreOptions{ + CommonRestoreOptions: CommonRestoreOptions{TargetNamespace: "tenant-foo"}, + }) + if target.IsRenamed { + t.Error("IsRenamed should be false when names match") + } + }) + + t.Run("different name is renamed", func(t *testing.T) { + rj := &backupsv1alpha1.RestoreJob{ + ObjectMeta: metav1.ObjectMeta{Name: "rj", Namespace: "tenant-root"}, + Spec: backupsv1alpha1.RestoreJobSpec{ + BackupRef: corev1.LocalObjectReference{Name: "bk"}, + TargetApplicationRef: &corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("apps.cozystack.io"), + Kind: "VMInstance", + Name: "test-new", + }, + }, + } + target := resolveRestoreTarget(rj, backup, RestoreOptions{ + CommonRestoreOptions: CommonRestoreOptions{TargetNamespace: "tenant-foo"}, + }) + if !target.IsRenamed { + t.Error("IsRenamed should be true when names differ") + } + if target.AppName != "test-new" { + t.Errorf("AppName = %q, want 'test-new'", target.AppName) + } + if !target.IsCopy { + t.Error("IsCopy should be true") + } + }) + + t.Run("no targetApplicationRef is not renamed", func(t *testing.T) { + rj := &backupsv1alpha1.RestoreJob{ + ObjectMeta: metav1.ObjectMeta{Name: "rj", Namespace: "tenant-root"}, + Spec: backupsv1alpha1.RestoreJobSpec{ + BackupRef: corev1.LocalObjectReference{Name: "bk"}, + }, + } + target := resolveRestoreTarget(rj, backup, RestoreOptions{ + CommonRestoreOptions: CommonRestoreOptions{TargetNamespace: "tenant-foo"}, + }) + if target.IsRenamed { + t.Error("IsRenamed should be false when targetApplicationRef is nil") + } + if target.AppName != "test-alpine" { + t.Errorf("AppName = %q, want 'test-alpine'", target.AppName) + } + }) +} + +// makeUnstructuredHelmRelease creates an unstructured HelmRelease for dynamic client tests. +func makeUnstructuredHelmRelease(name, namespace string, labels map[string]string) *unstructured.Unstructured { + hr := &unstructured.Unstructured{} + hr.SetAPIVersion("helm.toolkit.fluxcd.io/v2") + hr.SetKind("HelmRelease") + hr.SetName(name) + hr.SetNamespace(namespace) + hr.SetLabels(labels) + return hr +} + +func TestPostRestoreRename_RenamesHelmRelease(t *testing.T) { + ns := "tenant-foo" + sourceHR := makeUnstructuredHelmRelease("vm-instance-test-alpine", ns, map[string]string{ + appNameLabel: "test-alpine", + "app.kubernetes.io/instance": "vm-instance-test-alpine", + "helm.toolkit.fluxcd.io/name": "vm-instance-test-alpine", + }) + + restoreJob := &backupsv1alpha1.RestoreJob{ + ObjectMeta: metav1.ObjectMeta{Name: "rj-rename", Namespace: "tenant-root"}, + Spec: backupsv1alpha1.RestoreJobSpec{ + BackupRef: corev1.LocalObjectReference{Name: "bk"}, + }, + } + backup := &backupsv1alpha1.Backup{ + ObjectMeta: metav1.ObjectMeta{Name: "bk", Namespace: "tenant-root"}, + Spec: backupsv1alpha1.BackupSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("apps.cozystack.io"), + Kind: "VMInstance", + Name: "test-alpine", + }, + }, + } + target := restoreTarget{ + Namespace: ns, + AppName: "test-new", + AppKind: "VMInstance", + IsCopy: true, + IsRenamed: true, + } + + reconciler := newTestRestoreJobReconcilerWithDynamic(t, []runtime.Object{sourceHR}, restoreJob, backup) + ctx := context.Background() + + err := reconciler.postRestoreRename(ctx, restoreJob, backup, target) + if err != nil { + t.Fatalf("postRestoreRename() error = %v", err) + } + + hrClient := reconciler.Resource(helmReleaseGVR).Namespace(ns) + + // New HelmRelease should exist with target name + newHR, err := hrClient.Get(ctx, "vm-instance-test-new", metav1.GetOptions{}) + if err != nil { + t.Fatalf("new HelmRelease vm-instance-test-new not found: %v", err) + } + + // Check labels were updated + labels := newHR.GetLabels() + if labels[appNameLabel] != "test-new" { + t.Errorf("label %s = %q, want 'test-new'", appNameLabel, labels[appNameLabel]) + } + if labels["app.kubernetes.io/instance"] != "vm-instance-test-new" { + t.Errorf("label app.kubernetes.io/instance = %q, want 'vm-instance-test-new'", labels["app.kubernetes.io/instance"]) + } + if labels["helm.toolkit.fluxcd.io/name"] != "vm-instance-test-new" { + t.Errorf("label helm.toolkit.fluxcd.io/name = %q, want 'vm-instance-test-new'", labels["helm.toolkit.fluxcd.io/name"]) + } + + // Old HelmRelease should be deleted + _, err = hrClient.Get(ctx, "vm-instance-test-alpine", metav1.GetOptions{}) + if err == nil { + t.Error("old HelmRelease vm-instance-test-alpine should have been deleted") + } +} + +func TestPostRestoreRename_SkipsWhenSourceNotFound(t *testing.T) { + restoreJob := &backupsv1alpha1.RestoreJob{ + ObjectMeta: metav1.ObjectMeta{Name: "rj-rename", Namespace: "tenant-root"}, + Spec: backupsv1alpha1.RestoreJobSpec{ + BackupRef: corev1.LocalObjectReference{Name: "bk"}, + }, + } + backup := &backupsv1alpha1.Backup{ + ObjectMeta: metav1.ObjectMeta{Name: "bk", Namespace: "tenant-root"}, + Spec: backupsv1alpha1.BackupSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("apps.cozystack.io"), + Kind: "VMInstance", + Name: "test-alpine", + }, + }, + } + target := restoreTarget{ + Namespace: "tenant-foo", + AppName: "test-new", + AppKind: "VMInstance", + IsCopy: true, + IsRenamed: true, + } + + // No HelmRelease in dynamic client — should skip gracefully + reconciler := newTestRestoreJobReconcilerWithDynamic(t, nil, restoreJob, backup) + ctx := context.Background() + + err := reconciler.postRestoreRename(ctx, restoreJob, backup, target) + if err != nil { + t.Fatalf("postRestoreRename() should skip gracefully when source HR not found, got error: %v", err) + } +} + +func TestPostRestoreRename_IdempotentWhenTargetExists(t *testing.T) { + ns := "tenant-foo" + sourceHR := makeUnstructuredHelmRelease("vm-instance-test-alpine", ns, map[string]string{ + appNameLabel: "test-alpine", + }) + // Target already exists (e.g. from a previous reconcile) + targetHR := makeUnstructuredHelmRelease("vm-instance-test-new", ns, map[string]string{ + appNameLabel: "test-new", + }) + + restoreJob := &backupsv1alpha1.RestoreJob{ + ObjectMeta: metav1.ObjectMeta{Name: "rj-rename", Namespace: "tenant-root"}, + Spec: backupsv1alpha1.RestoreJobSpec{ + BackupRef: corev1.LocalObjectReference{Name: "bk"}, + }, + } + backup := &backupsv1alpha1.Backup{ + ObjectMeta: metav1.ObjectMeta{Name: "bk", Namespace: "tenant-root"}, + Spec: backupsv1alpha1.BackupSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("apps.cozystack.io"), + Kind: "VMInstance", + Name: "test-alpine", + }, + }, + } + target := restoreTarget{ + Namespace: ns, + AppName: "test-new", + AppKind: "VMInstance", + IsCopy: true, + IsRenamed: true, + } + + reconciler := newTestRestoreJobReconcilerWithDynamic(t, []runtime.Object{sourceHR, targetHR}, restoreJob, backup) + ctx := context.Background() + + err := reconciler.postRestoreRename(ctx, restoreJob, backup, target) + if err != nil { + t.Fatalf("postRestoreRename() should be idempotent, got error: %v", err) + } + + hrClient := reconciler.Resource(helmReleaseGVR).Namespace(ns) + + // Target should still exist + _, err = hrClient.Get(ctx, "vm-instance-test-new", metav1.GetOptions{}) + if err != nil { + t.Fatalf("target HelmRelease should exist: %v", err) + } + + // Source should be deleted + _, err = hrClient.Get(ctx, "vm-instance-test-alpine", metav1.GetOptions{}) + if err == nil { + t.Error("source HelmRelease should have been deleted") + } +} + +// --- failIfTargetExists enforcement tests --- + +func TestTargetHelmReleaseExists_ReturnsTrueWhenPresent(t *testing.T) { + ns := "tenant-copy" + hr := makeUnstructuredHelmRelease("vm-instance-test", ns, nil) + + restoreJob := &backupsv1alpha1.RestoreJob{ + ObjectMeta: metav1.ObjectMeta{Name: "rj", Namespace: "tenant-root"}, + } + backup := &backupsv1alpha1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "bk", Namespace: "tenant-root"}} + + reconciler := newTestRestoreJobReconcilerWithDynamic(t, []runtime.Object{hr}, restoreJob, backup) + ctx := context.Background() + + exists, err := reconciler.targetHelmReleaseExists(ctx, vmInstanceKind, "vm-instance-test", ns) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !exists { + t.Error("expected exists=true when HelmRelease is present") + } +} + +func TestTargetHelmReleaseExists_ReturnsFalseWhenAbsent(t *testing.T) { + ns := "tenant-copy" + restoreJob := &backupsv1alpha1.RestoreJob{ + ObjectMeta: metav1.ObjectMeta{Name: "rj", Namespace: "tenant-root"}, + } + backup := &backupsv1alpha1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "bk", Namespace: "tenant-root"}} + + reconciler := newTestRestoreJobReconcilerWithDynamic(t, nil, restoreJob, backup) + ctx := context.Background() + + exists, err := reconciler.targetHelmReleaseExists(ctx, vmInstanceKind, "vm-instance-test", ns) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if exists { + t.Error("expected exists=false when HelmRelease is absent") + } +} + +func TestTargetHelmReleaseExists_UnsupportedKindViaHelper(t *testing.T) { + ns := "tenant-copy" + // Even though a HR exists, unsupported kinds produce an empty hrName + // from helmReleaseNameForApp, which makes targetHelmReleaseExists return false. + hr := makeUnstructuredHelmRelease("vm-instance-test", ns, nil) + + restoreJob := &backupsv1alpha1.RestoreJob{ + ObjectMeta: metav1.ObjectMeta{Name: "rj", Namespace: "tenant-root"}, + } + backup := &backupsv1alpha1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "bk", Namespace: "tenant-root"}} + + reconciler := newTestRestoreJobReconcilerWithDynamic(t, []runtime.Object{hr}, restoreJob, backup) + ctx := context.Background() + + // Unsupported kinds get empty hrName from the helper + hrName := helmReleaseNameForApp("MariaDB", "mydb") + exists, err := reconciler.targetHelmReleaseExists(ctx, "MariaDB", hrName, ns) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if exists { + t.Error("expected exists=false for unsupported kind (empty hrName)") + } +} + +// --- createResourceModifiersConfigMap tests --- + +func makeTestBackup(ns, appName, appKind string) *backupsv1alpha1.Backup { + return &backupsv1alpha1.Backup{ + ObjectMeta: metav1.ObjectMeta{Name: "bk", Namespace: ns}, + Spec: backupsv1alpha1.BackupSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("apps.cozystack.io"), + Kind: appKind, + Name: appName, + }, + }, + } +} + +func makeTestRestoreJob(ns, name string) *backupsv1alpha1.RestoreJob { + return &backupsv1alpha1.RestoreJob{ + ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: ns}, + Spec: backupsv1alpha1.RestoreJobSpec{ + BackupRef: corev1.LocalObjectReference{Name: "bk"}, + }, + } +} + +func makeVMInstanceUR(ip, mac string, dvs []backupsv1alpha1.DataVolumeResource) *runtime.RawExtension { + data := vmInstanceResources{IP: ip, MAC: mac, DataVolumes: dvs} + raw, _ := json.Marshal(data) + return &runtime.RawExtension{Raw: raw} +} + +func TestCreateResourceModifiersConfigMap_InPlace_WithOVN(t *testing.T) { + ns := "tenant-root" + restoreJob := makeTestRestoreJob(ns, "rj-inplace") + backup := makeTestBackup(ns, "test-vm", "VMInstance") + + ur := makeVMInstanceUR("10.0.0.5", "aa:bb:cc:dd:ee:ff", nil) + target := restoreTarget{Namespace: ns, AppName: "test-vm", AppKind: "VMInstance", IsCopy: false} + opts := RestoreOptions{} // defaults: keepOriginalIpAndMac=true + + reconciler := newTestRestoreJobReconciler(t, restoreJob, backup) + ctx := context.Background() + + cm, err := reconciler.createResourceModifiersConfigMap(ctx, restoreJob, backup, ur, target, opts) + if err != nil { + t.Fatalf("createResourceModifiersConfigMap() error = %v", err) + } + if cm == nil { + t.Fatal("expected non-nil ConfigMap") + } + + rulesYAML, ok := cm.Data["resource-modifier-rules.yaml"] + if !ok { + t.Fatal("ConfigMap missing resource-modifier-rules.yaml key") + } + + // Should contain PVC adoption annotation + if !containsString(rulesYAML, cdiAllowClaimAdoption) { + t.Error("expected PVC adoption annotation in rules") + } + // Should contain OVN IP annotation (keepOriginalIpAndMac defaults to true) + if !containsString(rulesYAML, ovnIPAnnotation) { + t.Error("expected OVN IP annotation in rules for in-place restore with IP") + } + // Should NOT contain selector removal (in-place, not copy) + if containsString(rulesYAML, "/spec/selector") { + t.Error("selector removal rule should not be present for in-place restore") + } +} + +func TestCreateResourceModifiersConfigMap_Copy_NoOVN(t *testing.T) { + sourceNS := "tenant-root" + targetNS := "tenant-copy" + restoreJob := makeTestRestoreJob(sourceNS, "rj-copy") + backup := makeTestBackup(sourceNS, "test-vm", "VMInstance") + + // Copy restore: keepOriginalIpAndMac=false, no OVN annotations on copy + opts := RestoreOptions{ + KeepOriginalIpAndMac: boolPtr(false), + } + ur := makeVMInstanceUR("10.0.0.5", "aa:bb:cc:dd:ee:ff", nil) + target := restoreTarget{Namespace: targetNS, AppName: "test-vm", AppKind: "VMInstance", IsCopy: true} + + reconciler := newTestRestoreJobReconciler(t, restoreJob, backup) + ctx := context.Background() + + cm, err := reconciler.createResourceModifiersConfigMap(ctx, restoreJob, backup, ur, target, opts) + if err != nil { + t.Fatalf("createResourceModifiersConfigMap() error = %v", err) + } + + rulesYAML := cm.Data["resource-modifier-rules.yaml"] + + // Should contain PVC adoption annotation + if !containsString(rulesYAML, cdiAllowClaimAdoption) { + t.Error("expected PVC adoption annotation in rules") + } + // Should contain merge patch nulling out selector and volumeName (copy restore) + if !containsString(rulesYAML, "selector: null") { + t.Error("expected selector: null merge patch for copy restore") + } + if !containsString(rulesYAML, "volumeName: null") { + t.Error("expected volumeName: null merge patch for copy restore") + } + // Should NOT contain OVN annotations (keepOriginalIpAndMac=false) + if containsString(rulesYAML, ovnIPAnnotation) { + t.Error("OVN IP annotation should not be present when keepOriginalIpAndMac=false") + } +} + +func TestCreateResourceModifiersConfigMap_AlreadyExists_IsIdempotent(t *testing.T) { + ns := "tenant-root" + restoreJob := makeTestRestoreJob(ns, "rj-idem") + backup := makeTestBackup(ns, "test-vm", "VMInstance") + + target := restoreTarget{Namespace: ns, AppName: "test-vm", AppKind: "VMInstance", IsCopy: false} + opts := RestoreOptions{} + ur := makeVMInstanceUR("", "", nil) + + reconciler := newTestRestoreJobReconciler(t, restoreJob, backup) + ctx := context.Background() + + // First call creates the ConfigMap. + cm1, err := reconciler.createResourceModifiersConfigMap(ctx, restoreJob, backup, ur, target, opts) + if err != nil { + t.Fatalf("first call error = %v", err) + } + + // Second call must not error (AlreadyExists → update path). + cm2, err := reconciler.createResourceModifiersConfigMap(ctx, restoreJob, backup, ur, target, opts) + if err != nil { + t.Fatalf("second call error = %v", err) + } + if cm1.Name != cm2.Name { + t.Errorf("ConfigMap name changed: %q → %q", cm1.Name, cm2.Name) + } +} + +// --- helmReleaseNameForApp tests --- + +func TestHelmReleaseNameForApp(t *testing.T) { + tests := []struct { + kind, name, want string + }{ + {vmInstanceKind, "test-alpine", "vm-instance-test-alpine"}, + {vmDiskAppKind, "ubuntu-source", "vm-disk-ubuntu-source"}, + {"MariaDB", "mydb", ""}, + } + for _, tt := range tests { + t.Run(tt.kind+"/"+tt.name, func(t *testing.T) { + got := helmReleaseNameForApp(tt.kind, tt.name) + if got != tt.want { + t.Errorf("helmReleaseNameForApp(%q, %q) = %q, want %q", tt.kind, tt.name, got, tt.want) + } + }) + } +} + +func TestTargetHelmReleaseExists_VMDisk(t *testing.T) { + ns := "tenant-copy" + hr := makeUnstructuredHelmRelease("vm-disk-ubuntu-source", ns, nil) + + restoreJob := &backupsv1alpha1.RestoreJob{ + ObjectMeta: metav1.ObjectMeta{Name: "rj", Namespace: "tenant-root"}, + } + backup := &backupsv1alpha1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "bk", Namespace: "tenant-root"}} + + reconciler := newTestRestoreJobReconcilerWithDynamic(t, []runtime.Object{hr}, restoreJob, backup) + ctx := context.Background() + + hrName := helmReleaseNameForApp(vmDiskAppKind, "ubuntu-source") + exists, err := reconciler.targetHelmReleaseExists(ctx, vmDiskAppKind, hrName, ns) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !exists { + t.Error("expected exists=true for VMDisk HelmRelease") + } +} + +func TestTargetHelmReleaseExists_UnsupportedKind_ReturnsFalse(t *testing.T) { + restoreJob := &backupsv1alpha1.RestoreJob{ + ObjectMeta: metav1.ObjectMeta{Name: "rj", Namespace: "tenant-root"}, + } + backup := &backupsv1alpha1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "bk", Namespace: "tenant-root"}} + + reconciler := newTestRestoreJobReconcilerWithDynamic(t, nil, restoreJob, backup) + ctx := context.Background() + + // Unsupported kinds produce empty hrName which returns false + hrName := helmReleaseNameForApp("MariaDB", "mydb") + exists, err := reconciler.targetHelmReleaseExists(ctx, "MariaDB", hrName, "tenant-copy") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if exists { + t.Error("expected exists=false for unsupported kind") + } +} + +// --- ResourceModifier merge patch test for copy restore --- + +func TestCreateResourceModifiersConfigMap_Copy_UsesMergePatchForPVC(t *testing.T) { + sourceNS := "tenant-root" + targetNS := "tenant-copy" + restoreJob := makeTestRestoreJob(sourceNS, "rj-copy-merge") + backup := makeTestBackup(sourceNS, "test-vm", "VMInstance") + + opts := RestoreOptions{KeepOriginalIpAndMac: boolPtr(false)} + ur := makeVMInstanceUR("", "", nil) + target := restoreTarget{Namespace: targetNS, AppName: "test-vm", AppKind: "VMInstance", IsCopy: true} + + reconciler := newTestRestoreJobReconciler(t, restoreJob, backup) + ctx := context.Background() + + cm, err := reconciler.createResourceModifiersConfigMap(ctx, restoreJob, backup, ur, target, opts) + if err != nil { + t.Fatalf("createResourceModifiersConfigMap() error = %v", err) + } + + rulesYAML := cm.Data["resource-modifier-rules.yaml"] + + // Should use merge patch (patchData with null), NOT JSON patch (operation: remove) + if containsString(rulesYAML, "operation: remove") { + t.Error("should use merge patch instead of JSON Patch remove for PVC fields") + } + // The merge patch should null out selector and volumeName + if !containsString(rulesYAML, "selector: null") { + t.Error("expected selector: null in merge patch") + } + if !containsString(rulesYAML, "volumeName: null") { + t.Error("expected volumeName: null in merge patch") + } +} + +// containsString reports whether substr appears in s. +func containsString(s, substr string) bool { + if len(substr) == 0 || len(s) < len(substr) { + return false + } + for i := 0; i <= len(s)-len(substr); i++ { + if s[i:i+len(substr)] == substr { + return true + } + } + return false +} diff --git a/internal/controller/dashboard/README.md b/internal/controller/dashboard/README.md index c3b2152e..2efe9f45 100644 --- a/internal/controller/dashboard/README.md +++ b/internal/controller/dashboard/README.md @@ -156,7 +156,7 @@ menuItems = append(menuItems, map[string]any{ map[string]any{ "key": "{plural}", "label": "{ResourceLabel}", - "link": "/openapi-ui/{clusterName}/{namespace}/api-table/{group}/{version}/{plural}", + "link": "/openapi-ui/{cluster}/{namespace}/api-table/{group}/{version}/{plural}", }, }, }), @@ -174,7 +174,7 @@ menuItems = append(menuItems, map[string]any{ **Important Notes**: - The sidebar tag (`{lowercase-kind}-sidebar`) must match what the Factory uses -- The link format: `/openapi-ui/{clusterName}/{namespace}/api-table/{group}/{version}/{plural}` +- The link format: `/openapi-ui/{cluster}/{namespace}/api-table/{group}/{version}/{plural}` - All sidebars share the same `keysAndTags` and `menuItems`, so changes affect all sidebar instances ### Step 4: Verify Integration diff --git a/internal/controller/dashboard/customformsoverride.go b/internal/controller/dashboard/customformsoverride.go index dfbccf5c..0a20ae71 100644 --- a/internal/controller/dashboard/customformsoverride.go +++ b/internal/controller/dashboard/customformsoverride.go @@ -46,8 +46,11 @@ func (m *Manager) ensureCustomFormsOverride(ctx context.Context, crd *cozyv1alph } } - // Build schema with multilineString for string fields without enum + // Parse OpenAPI schema once for reuse l := log.FromContext(ctx) + openAPIProps := parseOpenAPIProperties(crd.Spec.Application.OpenAPISchema) + + // Build schema with multilineString for string fields without enum schema, err := buildMultilineStringSchema(crd.Spec.Application.OpenAPISchema) if err != nil { // If schema parsing fails, log the error and use an empty schema @@ -55,6 +58,12 @@ func (m *Manager) ensureCustomFormsOverride(ctx context.Context, crd *cozyv1alph schema = map[string]any{} } + // Override specific fields with API-backed dropdowns (listInput type) + applyListInputOverrides(schema, kind, openAPIProps) + + // Hide deprecated fields from the UI + hidden = append(hidden, hiddenDeprecatedFields(kind)...) + spec := map[string]any{ "customizationId": customizationID, "hidden": hidden, @@ -132,7 +141,10 @@ func (m *Manager) ensureCFOMapping(ctx context.Context, crd *cozyv1alpha1.Applic } // buildMultilineStringSchema parses OpenAPI schema and creates schema with multilineString -// for all string fields inside spec that don't have enum +// for all string fields inside spec that don't have enum. +// It handles two structures: +// - properties.spec.properties (most resources) +// - properties.properties (VMDisk and similar resources without spec wrapper) func buildMultilineStringSchema(openAPISchema string) (map[string]any, error) { if openAPISchema == "" { return map[string]any{}, nil @@ -152,15 +164,25 @@ func buildMultilineStringSchema(openAPISchema string) (map[string]any, error) { "properties": map[string]any{}, } - // Check if there's a spec property - specProp, ok := props["spec"].(map[string]any) - if !ok { - return map[string]any{}, nil + var specProps map[string]any + var hasSpec bool + + // First try to find properties under spec + if specProp, ok := props["spec"].(map[string]any); ok { + specProps, hasSpec = specProp["properties"].(map[string]any) } - specProps, ok := specProp["properties"].(map[string]any) - if !ok { - return map[string]any{}, nil + // If no spec wrapper, use top-level properties directly (VMDisk pattern) + if !hasSpec { + specProps = props + // Still wrap in spec for consistency with applyListInputOverrides + schemaProps := schema["properties"].(map[string]any) + specSchema := map[string]any{ + "properties": map[string]any{}, + } + schemaProps["spec"] = specSchema + processSpecProperties(specProps, specSchema["properties"].(map[string]any)) + return schema, nil } // Create spec.properties structure in schema @@ -176,6 +198,206 @@ func buildMultilineStringSchema(openAPISchema string) (map[string]any, error) { return schema, nil } +// applyListInputOverrides injects listInput type overrides into the schema +// for fields that should be rendered as API-backed dropdowns in the dashboard. +// openAPIProps are the parsed top-level properties from the OpenAPI schema. +func applyListInputOverrides(schema map[string]any, kind string, openAPIProps map[string]any) { + switch kind { + case "VMInstance": + specProps := ensureSchemaPath(schema, "spec") + field := map[string]any{ + "type": "listInput", + "customProps": map[string]any{ + "valueUri": "/api/clusters/{cluster}/k8s/apis/instancetype.kubevirt.io/v1beta1/virtualmachineclusterinstancetypes", + "keysToValue": []any{"metadata", "name"}, + "keysToLabel": []any{"metadata", "name"}, + "allowEmpty": true, + }, + } + if prop, _ := openAPIProps["instanceType"].(map[string]any); prop != nil { + if def := prop["default"]; def != nil { + field["default"] = def + } + } + specProps["instanceType"] = field + + // Override disks[].name to be an API-backed dropdown listing VMDisk resources + disksItemProps := ensureArrayItemProps(specProps, "disks") + disksItemProps["name"] = map[string]any{ + "type": "listInput", + "customProps": map[string]any{ + "valueUri": "/api/clusters/{cluster}/k8s/apis/apps.cozystack.io/v1alpha1/namespaces/{namespace}/vmdisks", + "keysToValue": []any{"metadata", "name"}, + "keysToLabel": []any{"metadata", "name"}, + }, + } + + // Override networks[].name to be an API-backed dropdown listing NetworkAttachmentDefinitions + networksItemProps := ensureArrayItemProps(specProps, "networks") + networksItemProps["name"] = map[string]any{ + "type": "listInput", + "customProps": map[string]any{ + "valueUri": "/api/clusters/{cluster}/k8s/apis/k8s.cni.cncf.io/v1/namespaces/{namespace}/network-attachment-definitions", + "keysToValue": []any{"metadata", "name"}, + "keysToLabel": []any{"metadata", "name"}, + }, + } + + case "ClickHouse", "Harbor", "HTTPCache", "Kubernetes", "MariaDB", "MongoDB", + "NATS", "OpenBAO", "Postgres", "Qdrant", "RabbitMQ", "Redis": + specProps := ensureSchemaPath(schema, "spec") + specProps["storageClass"] = storageClassListInput() + + case "VMDisk": + specProps := ensureSchemaPath(schema, "spec") + specProps["storageClass"] = storageClassListInput() + + // Override source.image.name to be an API-backed dropdown listing default images + if sourceObj, ok := specProps["source"].(map[string]any); ok { + if imgProps, ok := sourceObj["properties"].(map[string]any); ok { + if imgName, ok := imgProps["image"].(map[string]any); ok { + if imgNameProps, ok := imgName["properties"].(map[string]any); ok { + imgNameProps["name"] = map[string]any{ + "type": "listInput", + "customProps": map[string]any{ + "valueUri": "/api/clusters/{cluster}/k8s/apis/cdi.kubevirt.io/v1beta1/namespaces/cozy-public/datavolumes", + "keysToValue": []any{"metadata", "annotations", "vm-default-images.cozystack.io/name"}, + "keysToLabel": []any{"metadata", "annotations", "vm-default-images.cozystack.io/description"}, + }, + } + } + } + // Override source.disk.name to be an API-backed dropdown listing VMDisk resources + if diskName, ok := imgProps["disk"].(map[string]any); ok { + if diskNameProps, ok := diskName["properties"].(map[string]any); ok { + diskNameProps["name"] = map[string]any{ + "type": "listInput", + "customProps": map[string]any{ + "valueUri": "/api/clusters/{cluster}/k8s/apis/apps.cozystack.io/v1alpha1/namespaces/{namespace}/vmdisks", + "keysToValue": []any{"metadata", "name"}, + "keysToLabel": []any{"metadata", "name"}, + }, + } + } + } + } + } + + case "FoundationDB": + storageProps := ensureSchemaPath(schema, "spec", "storage") + storageProps["storageClass"] = storageClassListInput() + + case "Kafka": + kafkaProps := ensureSchemaPath(schema, "spec", "kafka") + kafkaProps["storageClass"] = storageClassListInput() + zkProps := ensureSchemaPath(schema, "spec", "zookeeper") + zkProps["storageClass"] = storageClassListInput() + + case "Tenant": + specProps := ensureSchemaPath(schema, "spec") + specProps["schedulingClass"] = schedulingClassListInput() + } +} + +// hiddenDeprecatedFields returns hidden paths for deprecated fields that should not +// appear in the dashboard UI for the given kind. +func hiddenDeprecatedFields(kind string) []any { + switch kind { + case "VMInstance": + return []any{ + []any{"spec", "subnets"}, + } + } + return nil +} + +// storageClassListInput returns a listInput field config for a storageClass dropdown +// backed by the cluster's available StorageClasses. +func storageClassListInput() map[string]any { + return map[string]any{ + "type": "listInput", + "customProps": map[string]any{ + "valueUri": "/api/clusters/{cluster}/k8s/apis/storage.k8s.io/v1/storageclasses", + "keysToValue": []any{"metadata", "name"}, + "keysToLabel": []any{"metadata", "name"}, + }, + } +} + +// schedulingClassListInput returns a listInput field config for a schedulingClass dropdown +// backed by the cluster's available SchedulingClass CRs. +func schedulingClassListInput() map[string]any { + return map[string]any{ + "type": "listInput", + "customProps": map[string]any{ + "valueUri": "/api/clusters/{cluster}/k8s/apis/cozystack.io/v1alpha1/schedulingclasses", + "keysToValue": []any{"metadata", "name"}, + "keysToLabel": []any{"metadata", "name"}, + "allowEmpty": true, + }, + } +} + +// ensureArrayItemProps ensures that parentProps[fieldName].items.properties exists +// and returns the items properties map. Used for overriding fields inside array items. +func ensureArrayItemProps(parentProps map[string]any, fieldName string) map[string]any { + field, ok := parentProps[fieldName].(map[string]any) + if !ok { + field = map[string]any{} + parentProps[fieldName] = field + } + items, ok := field["items"].(map[string]any) + if !ok { + items = map[string]any{} + field["items"] = items + } + props, ok := items["properties"].(map[string]any) + if !ok { + props = map[string]any{} + items["properties"] = props + } + return props +} + +// parseOpenAPIProperties parses the top-level properties from an OpenAPI schema JSON string. +func parseOpenAPIProperties(openAPISchema string) map[string]any { + if openAPISchema == "" { + return nil + } + var root map[string]any + if err := json.Unmarshal([]byte(openAPISchema), &root); err != nil { + return nil + } + props, _ := root["properties"].(map[string]any) + return props +} + +// ensureSchemaPath ensures the nested properties structure exists in a schema +// and returns the innermost properties map. +// e.g. ensureSchemaPath(schema, "spec") returns schema["properties"]["spec"]["properties"] +func ensureSchemaPath(schema map[string]any, segments ...string) map[string]any { + current := schema + for _, seg := range segments { + props, ok := current["properties"].(map[string]any) + if !ok { + props = map[string]any{} + current["properties"] = props + } + child, ok := props[seg].(map[string]any) + if !ok { + child = map[string]any{} + props[seg] = child + } + current = child + } + props, ok := current["properties"].(map[string]any) + if !ok { + props = map[string]any{} + current["properties"] = props + } + return props +} + // processSpecProperties recursively processes spec properties and adds multilineString type // for string fields without enum func processSpecProperties(props map[string]any, schemaProps map[string]any) { diff --git a/internal/controller/dashboard/customformsoverride_test.go b/internal/controller/dashboard/customformsoverride_test.go index 9f7babe9..6a83d62e 100644 --- a/internal/controller/dashboard/customformsoverride_test.go +++ b/internal/controller/dashboard/customformsoverride_test.go @@ -169,3 +169,453 @@ func TestBuildMultilineStringSchemaInvalidJSON(t *testing.T) { t.Errorf("Expected nil schema for invalid JSON, got %v", schema) } } + +func TestApplyListInputOverrides_VMInstance(t *testing.T) { + openAPIProps := map[string]any{ + "instanceType": map[string]any{"type": "string", "default": "u1.medium"}, + } + + schema := map[string]any{} + applyListInputOverrides(schema, "VMInstance", openAPIProps) + + specProps := schema["properties"].(map[string]any)["spec"].(map[string]any)["properties"].(map[string]any) + instanceType, ok := specProps["instanceType"].(map[string]any) + if !ok { + t.Fatal("instanceType not found in schema.properties.spec.properties") + } + + if instanceType["type"] != "listInput" { + t.Errorf("expected type listInput, got %v", instanceType["type"]) + } + + if instanceType["default"] != "u1.medium" { + t.Errorf("expected default u1.medium, got %v", instanceType["default"]) + } + + customProps, ok := instanceType["customProps"].(map[string]any) + if !ok { + t.Fatal("customProps not found") + } + + expectedURI := "/api/clusters/{cluster}/k8s/apis/instancetype.kubevirt.io/v1beta1/virtualmachineclusterinstancetypes" + if customProps["valueUri"] != expectedURI { + t.Errorf("expected valueUri %s, got %v", expectedURI, customProps["valueUri"]) + } + + if customProps["allowEmpty"] != true { + t.Errorf("expected allowEmpty true, got %v", customProps["allowEmpty"]) + } + + // Check disks[].name is a listInput + disks, ok := specProps["disks"].(map[string]any) + if !ok { + t.Fatal("disks not found in schema.properties.spec.properties") + } + items, ok := disks["items"].(map[string]any) + if !ok { + t.Fatal("disks.items not found") + } + itemProps, ok := items["properties"].(map[string]any) + if !ok { + t.Fatal("disks.items.properties not found") + } + diskName, ok := itemProps["name"].(map[string]any) + if !ok { + t.Fatal("disks.items.properties.name not found") + } + if diskName["type"] != "listInput" { + t.Errorf("expected disks name type listInput, got %v", diskName["type"]) + } + diskCustomProps, ok := diskName["customProps"].(map[string]any) + if !ok { + t.Fatal("disks name customProps not found") + } + expectedDiskURI := "/api/clusters/{cluster}/k8s/apis/apps.cozystack.io/v1alpha1/namespaces/{namespace}/vmdisks" + if diskCustomProps["valueUri"] != expectedDiskURI { + t.Errorf("expected disks valueUri %s, got %v", expectedDiskURI, diskCustomProps["valueUri"]) + } + + // Check networks[].name is a listInput + networks, ok := specProps["networks"].(map[string]any) + if !ok { + t.Fatal("networks not found in schema.properties.spec.properties") + } + netItems, ok := networks["items"].(map[string]any) + if !ok { + t.Fatal("networks.items not found") + } + netItemProps, ok := netItems["properties"].(map[string]any) + if !ok { + t.Fatal("networks.items.properties not found") + } + netName, ok := netItemProps["name"].(map[string]any) + if !ok { + t.Fatal("networks.items.properties.name not found") + } + if netName["type"] != "listInput" { + t.Errorf("expected networks name type listInput, got %v", netName["type"]) + } + netCustomProps, ok := netName["customProps"].(map[string]any) + if !ok { + t.Fatal("networks name customProps not found") + } + expectedNetURI := "/api/clusters/{cluster}/k8s/apis/k8s.cni.cncf.io/v1/namespaces/{namespace}/network-attachment-definitions" + if netCustomProps["valueUri"] != expectedNetURI { + t.Errorf("expected networks valueUri %s, got %v", expectedNetURI, netCustomProps["valueUri"]) + } +} + +func TestHiddenDeprecatedFields_VMInstance(t *testing.T) { + hidden := hiddenDeprecatedFields("VMInstance") + if len(hidden) != 1 { + t.Fatalf("expected 1 hidden path, got %d", len(hidden)) + } + path, ok := hidden[0].([]any) + if !ok { + t.Fatal("hidden path is not []any") + } + if len(path) != 2 || path[0] != "spec" || path[1] != "subnets" { + t.Errorf("expected [spec subnets], got %v", path) + } +} + +func TestHiddenDeprecatedFields_UnknownKind(t *testing.T) { + hidden := hiddenDeprecatedFields("SomeOtherKind") + if hidden != nil { + t.Errorf("expected nil for unknown kind, got %v", hidden) + } +} + +func TestApplyListInputOverrides_StorageClassSimple(t *testing.T) { + for _, kind := range []string{ + "ClickHouse", "Harbor", "HTTPCache", "Kubernetes", "MariaDB", "MongoDB", + "NATS", "OpenBAO", "Postgres", "Qdrant", "RabbitMQ", "Redis", "VMDisk", + } { + t.Run(kind, func(t *testing.T) { + schema := map[string]any{} + applyListInputOverrides(schema, kind, map[string]any{}) + + specProps := schema["properties"].(map[string]any)["spec"].(map[string]any)["properties"].(map[string]any) + sc, ok := specProps["storageClass"].(map[string]any) + if !ok { + t.Fatalf("storageClass not found in spec.properties for kind %s", kind) + } + assertStorageClassListInput(t, sc) + }) + } +} + +func TestApplyListInputOverrides_StorageClassFoundationDB(t *testing.T) { + schema := map[string]any{} + applyListInputOverrides(schema, "FoundationDB", map[string]any{}) + + storageProps := schema["properties"].(map[string]any)["spec"].(map[string]any)["properties"].(map[string]any)["storage"].(map[string]any)["properties"].(map[string]any) + sc, ok := storageProps["storageClass"].(map[string]any) + if !ok { + t.Fatal("storageClass not found in spec.storage.properties") + } + assertStorageClassListInput(t, sc) +} + +func TestApplyListInputOverrides_VMDisk_SourceFields(t *testing.T) { + openAPISchema := `{ + "properties":{ + "optical":{"type":"boolean"}, + "source":{ + "type":"object", + "properties":{ + "image":{ + "type":"object", + "properties":{ + "name":{"type":"string"} + } + }, + "disk":{ + "type":"object", + "properties":{ + "name":{"type":"string"} + } + } + } + }, + "storage":{"type":"string"}, + "storageClass":{"type":"string"} + } + }` + + schema, err := buildMultilineStringSchema(openAPISchema) + if err != nil { + t.Fatalf("buildMultilineStringSchema failed: %v", err) + } + + applyListInputOverrides(schema, "VMDisk", map[string]any{}) + + specProps := schema["properties"].(map[string]any)["spec"].(map[string]any)["properties"].(map[string]any) + + // Check storageClass + sc, ok := specProps["storageClass"].(map[string]any) + if !ok { + t.Fatal("storageClass not found in spec.properties") + } + assertStorageClassListInput(t, sc) + + // Check source.image.name listInput + // Structure: specProps["source"]["properties"]["image"]["properties"]["name"] + sourceObj, ok := specProps["source"].(map[string]any) + if !ok { + t.Fatal("source not found in spec.properties") + } + sourceObjProps, ok := sourceObj["properties"].(map[string]any) + if !ok { + t.Fatal("source.properties not found") + } + imageObj, ok := sourceObjProps["image"].(map[string]any) + if !ok { + t.Fatal("image not found in source.properties") + } + imageObjProps, ok := imageObj["properties"].(map[string]any) + if !ok { + t.Fatal("image.properties not found") + } + imgName, ok := imageObjProps["name"].(map[string]any) + if !ok { + t.Fatal("name not found in image.properties") + } + if imgName["type"] != "listInput" { + t.Errorf("expected type listInput, got %v", imgName["type"]) + } + imgNameCustomProps, ok := imgName["customProps"].(map[string]any) + if !ok { + t.Fatal("name.customProps not found") + } + expectedImageURI := "/api/clusters/{cluster}/k8s/apis/cdi.kubevirt.io/v1beta1/namespaces/cozy-public/datavolumes" + if imgNameCustomProps["valueUri"] != expectedImageURI { + t.Errorf("expected valueUri %s, got %v", expectedImageURI, imgNameCustomProps["valueUri"]) + } + + // Check source.disk.name listInput + diskObj, ok := sourceObjProps["disk"].(map[string]any) + if !ok { + t.Fatal("disk not found in source.properties") + } + diskObjProps, ok := diskObj["properties"].(map[string]any) + if !ok { + t.Fatal("disk.properties not found") + } + diskName, ok := diskObjProps["name"].(map[string]any) + if !ok { + t.Fatal("name not found in disk.properties") + } + if diskName["type"] != "listInput" { + t.Errorf("expected type listInput, got %v", diskName["type"]) + } + diskNameCustomProps, ok := diskName["customProps"].(map[string]any) + if !ok { + t.Fatal("disk name.customProps not found") + } + expectedDiskURI := "/api/clusters/{cluster}/k8s/apis/apps.cozystack.io/v1alpha1/namespaces/{namespace}/vmdisks" + if diskNameCustomProps["valueUri"] != expectedDiskURI { + t.Errorf("expected valueUri %s, got %v", expectedDiskURI, diskNameCustomProps["valueUri"]) + } +} + +func TestApplyListInputOverrides_StorageClassKafka(t *testing.T) { + schema := map[string]any{} + applyListInputOverrides(schema, "Kafka", map[string]any{}) + + specProps := schema["properties"].(map[string]any)["spec"].(map[string]any)["properties"].(map[string]any) + + kafkaSC, ok := specProps["kafka"].(map[string]any)["properties"].(map[string]any)["storageClass"].(map[string]any) + if !ok { + t.Fatal("storageClass not found in spec.kafka.properties") + } + assertStorageClassListInput(t, kafkaSC) + + zkSC, ok := specProps["zookeeper"].(map[string]any)["properties"].(map[string]any)["storageClass"].(map[string]any) + if !ok { + t.Fatal("storageClass not found in spec.zookeeper.properties") + } + assertStorageClassListInput(t, zkSC) +} + +// assertStorageClassListInput verifies that a field is a correctly configured storageClass listInput. +func assertStorageClassListInput(t *testing.T, field map[string]any) { + t.Helper() + if field["type"] != "listInput" { + t.Errorf("expected type listInput, got %v", field["type"]) + } + customProps, ok := field["customProps"].(map[string]any) + if !ok { + t.Fatal("customProps not found") + } + expectedURI := "/api/clusters/{cluster}/k8s/apis/storage.k8s.io/v1/storageclasses" + if customProps["valueUri"] != expectedURI { + t.Errorf("expected valueUri %s, got %v", expectedURI, customProps["valueUri"]) + } +} + +func TestApplyListInputOverrides_UnknownKind(t *testing.T) { + schema := map[string]any{} + applyListInputOverrides(schema, "SomeOtherKind", map[string]any{}) + + if len(schema) != 0 { + t.Errorf("expected empty schema for unknown kind, got %v", schema) + } +} + +func TestApplyListInputOverrides_NoDefault(t *testing.T) { + openAPIProps := map[string]any{ + "instanceType": map[string]any{"type": "string"}, + } + + schema := map[string]any{} + applyListInputOverrides(schema, "VMInstance", openAPIProps) + + specProps := schema["properties"].(map[string]any)["spec"].(map[string]any)["properties"].(map[string]any) + instanceType := specProps["instanceType"].(map[string]any) + + if _, exists := instanceType["default"]; exists { + t.Errorf("expected no default key, got %v", instanceType["default"]) + } +} + +func TestApplyListInputOverrides_MergesWithExistingSchema(t *testing.T) { + openAPIProps := map[string]any{ + "instanceType": map[string]any{"type": "string", "default": "u1.medium"}, + } + + // Simulate schema that already has spec.properties from buildMultilineStringSchema + schema := map[string]any{ + "properties": map[string]any{ + "spec": map[string]any{ + "properties": map[string]any{ + "otherField": map[string]any{"type": "multilineString"}, + }, + }, + }, + } + applyListInputOverrides(schema, "VMInstance", openAPIProps) + + specProps := schema["properties"].(map[string]any)["spec"].(map[string]any)["properties"].(map[string]any) + + // instanceType should be added + if _, ok := specProps["instanceType"].(map[string]any); !ok { + t.Fatal("instanceType not found after override") + } + + // otherField should be preserved + otherField, ok := specProps["otherField"].(map[string]any) + if !ok { + t.Fatal("otherField was lost after override") + } + if otherField["type"] != "multilineString" { + t.Errorf("otherField type changed, got %v", otherField["type"]) + } +} + +func TestParseOpenAPIProperties(t *testing.T) { + t.Run("extracts properties", func(t *testing.T) { + props := parseOpenAPIProperties(`{"type":"object","properties":{"instanceType":{"type":"string","default":"u1.medium"}}}`) + field, _ := props["instanceType"].(map[string]any) + if field["default"] != "u1.medium" { + t.Errorf("expected default u1.medium, got %v", field["default"]) + } + }) + + t.Run("empty string", func(t *testing.T) { + if props := parseOpenAPIProperties(""); props != nil { + t.Errorf("expected nil, got %v", props) + } + }) + + t.Run("invalid JSON", func(t *testing.T) { + if props := parseOpenAPIProperties("{bad"); props != nil { + t.Errorf("expected nil, got %v", props) + } + }) + + t.Run("no properties key", func(t *testing.T) { + if props := parseOpenAPIProperties(`{"type":"object"}`); props != nil { + t.Errorf("expected nil, got %v", props) + } + }) +} + +func TestEnsureSchemaPath(t *testing.T) { + t.Run("creates path from empty schema", func(t *testing.T) { + schema := map[string]any{} + props := ensureSchemaPath(schema, "spec") + + props["field"] = "value" + + // Verify structure: schema.properties.spec.properties.field + got := schema["properties"].(map[string]any)["spec"].(map[string]any)["properties"].(map[string]any)["field"] + if got != "value" { + t.Errorf("expected value, got %v", got) + } + }) + + t.Run("preserves existing nested properties", func(t *testing.T) { + schema := map[string]any{ + "properties": map[string]any{ + "spec": map[string]any{ + "properties": map[string]any{ + "existing": "keep", + }, + }, + }, + } + props := ensureSchemaPath(schema, "spec") + + if props["existing"] != "keep" { + t.Errorf("existing property lost, got %v", props["existing"]) + } + }) + + t.Run("multi-level path", func(t *testing.T) { + schema := map[string]any{} + props := ensureSchemaPath(schema, "spec", "nested") + + props["deep"] = true + + got := schema["properties"].(map[string]any)["spec"].(map[string]any)["properties"].(map[string]any)["nested"].(map[string]any)["properties"].(map[string]any)["deep"] + if got != true { + t.Errorf("expected true, got %v", got) + } + }) +} + +func TestEnsureArrayItemProps(t *testing.T) { + t.Run("creates from empty parent", func(t *testing.T) { + parent := map[string]any{} + props := ensureArrayItemProps(parent, "disks") + + props["name"] = map[string]any{"type": "listInput"} + + got := parent["disks"].(map[string]any)["items"].(map[string]any)["properties"].(map[string]any)["name"].(map[string]any)["type"] + if got != "listInput" { + t.Errorf("expected listInput, got %v", got) + } + }) + + t.Run("preserves existing item properties", func(t *testing.T) { + parent := map[string]any{ + "disks": map[string]any{ + "items": map[string]any{ + "properties": map[string]any{ + "bus": map[string]any{"type": "string"}, + }, + }, + }, + } + props := ensureArrayItemProps(parent, "disks") + props["name"] = map[string]any{"type": "listInput"} + + if props["bus"].(map[string]any)["type"] != "string" { + t.Error("existing bus property was lost") + } + if props["name"].(map[string]any)["type"] != "listInput" { + t.Error("name property was not added") + } + }) +} diff --git a/internal/controller/dashboard/factory.go b/internal/controller/dashboard/factory.go index 66ee111d..fe0cadfd 100644 --- a/internal/controller/dashboard/factory.go +++ b/internal/controller/dashboard/factory.go @@ -47,6 +47,7 @@ func (m *Manager) ensureFactory(ctx context.Context, crd *cozyv1alpha1.Applicati if prefix, ok := vncTabPrefix(kind); ok { tabs = append(tabs, vncTab(prefix)) } + tabs = append(tabs, eventsTab(kind)) tabs = append(tabs, yamlTab(g, v, plural)) // Use unified factory creation @@ -358,6 +359,37 @@ func secretsTab(kind string) map[string]any { } } +// eventsTab shows Kubernetes Events scoped to the application's namespace. +// Events are namespace-scoped because Kubernetes Events don't carry application +// labels and cannot be filtered by label selector. In Cozystack's multi-tenancy +// model, each tenant namespace maps to a single application scope, so namespace +// filtering provides the correct event scope. +// For Tenant applications, events are fetched from status.namespace (the tenant's +// own namespace) instead of the parent namespace where the Tenant object lives. +func eventsTab(kind string) map[string]any { + nsPlaceholder := "{3}" + if kind == "Tenant" { + nsPlaceholder = "{reqsJsonPath[0]['.status.namespace']}" + } + return map[string]any{ + "key": "events", + "label": "Events", + "children": []any{ + map[string]any{ + "type": "EnrichedTable", + "data": map[string]any{ + "id": "events-table", + "fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/" + nsPlaceholder + "/events", + "cluster": "{2}", + "baseprefix": "/openapi-ui", + "customizationId": "factory-details-events", + "pathToItems": []any{"items"}, + }, + }, + }, + } +} + func yamlTab(group, version, plural string) map[string]any { return map[string]any{ "key": "yaml", @@ -582,15 +614,14 @@ type factoryFlags struct { Secrets bool } -// factoryFeatureFlags tries several conventional locations so you can evolve the API -// without breaking the controller. Defaults are false (hidden). +// factoryFeatureFlags determines which tabs to show based on whether the +// ApplicationDefinition has non-empty Include resource selectors. +// Workloads tab is always shown. func factoryFeatureFlags(crd *cozyv1alpha1.ApplicationDefinition) factoryFlags { - var f factoryFlags - - f.Workloads = true - f.Ingresses = true - f.Services = true - f.Secrets = true - - return f + return factoryFlags{ + Workloads: true, + Ingresses: len(crd.Spec.Ingresses.Include) > 0, + Services: len(crd.Spec.Services.Include) > 0, + Secrets: len(crd.Spec.Secrets.Include) > 0, + } } diff --git a/internal/controller/dashboard/factory_test.go b/internal/controller/dashboard/factory_test.go new file mode 100644 index 00000000..b3742564 --- /dev/null +++ b/internal/controller/dashboard/factory_test.go @@ -0,0 +1,60 @@ +package dashboard + +import ( + "testing" +) + +func TestEventsTab_Structure(t *testing.T) { + tab := eventsTab("PostgreSQL") + + if tab["key"] != "events" { + t.Errorf("expected key=events, got %v", tab["key"]) + } + if tab["label"] != "Events" { + t.Errorf("expected label=Events, got %v", tab["label"]) + } + + children, ok := tab["children"].([]any) + if !ok || len(children) != 1 { + t.Fatal("expected exactly 1 child in events tab") + } + + table, ok := children[0].(map[string]any) + if !ok { + t.Fatal("child is not a map") + } + if table["type"] != "EnrichedTable" { + t.Errorf("expected type=EnrichedTable, got %v", table["type"]) + } + + data, ok := table["data"].(map[string]any) + if !ok { + t.Fatal("table data is not a map") + } + if data["id"] != "events-table" { + t.Errorf("expected id=events-table, got %v", data["id"]) + } + if data["fetchUrl"] != "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/events" { + t.Errorf("unexpected fetchUrl for non-Tenant: %v", data["fetchUrl"]) + } + if data["customizationId"] != "factory-details-events" { + t.Errorf("expected customizationId=factory-details-events, got %v", data["customizationId"]) + } + + pathToItems, ok := data["pathToItems"].([]any) + if !ok || len(pathToItems) != 1 || pathToItems[0] != "items" { + t.Errorf("expected pathToItems=[items], got %v", data["pathToItems"]) + } +} + +func TestEventsTab_TenantUsesStatusNamespace(t *testing.T) { + tab := eventsTab("Tenant") + children := tab["children"].([]any) + table := children[0].(map[string]any) + data := table["data"].(map[string]any) + + expectedURL := "/api/clusters/{2}/k8s/api/v1/namespaces/{reqsJsonPath[0]['.status.namespace']}/events" + if data["fetchUrl"] != expectedURL { + t.Errorf("expected Tenant fetchUrl to use status.namespace, got %v", data["fetchUrl"]) + } +} diff --git a/internal/controller/dashboard/manager.go b/internal/controller/dashboard/manager.go index b0a4cd1b..42c641ad 100644 --- a/internal/controller/dashboard/manager.go +++ b/internal/controller/dashboard/manager.go @@ -299,10 +299,6 @@ func (m *Manager) buildExpectedResourceSet(crds []cozyv1alpha1.ApplicationDefini // Add other stock sidebars that are created for each CRD stockSidebars := []string{ - "stock-instance-api-form", - "stock-instance-api-table", - "stock-instance-builtin-form", - "stock-instance-builtin-table", "stock-project-factory-marketplace", "stock-project-factory-workloadmonitor-details", "stock-project-api-form", @@ -311,6 +307,10 @@ func (m *Manager) buildExpectedResourceSet(crds []cozyv1alpha1.ApplicationDefini "stock-project-builtin-table", "stock-project-crd-form", "stock-project-crd-table", + "stock-instance-api-form", + "stock-instance-api-table", + "stock-instance-builtin-form", + "stock-instance-builtin-table", } for _, sidebarID := range stockSidebars { expected["Sidebar"][sidebarID] = true diff --git a/internal/controller/dashboard/marketplacepanel.go b/internal/controller/dashboard/marketplacepanel.go index fcfff799..14684afc 100644 --- a/internal/controller/dashboard/marketplacepanel.go +++ b/internal/controller/dashboard/marketplacepanel.go @@ -68,31 +68,46 @@ func (m *Manager) ensureMarketplacePanel(ctx context.Context, crd *cozyv1alpha1. tags[i] = t } - specMap := map[string]any{ - "description": d.Description, - "name": displayName, - "type": "nonCrd", - "apiGroup": "apps.cozystack.io", - "apiVersion": "v1alpha1", - "plural": app.Plural, // e.g., "buckets" - "disabled": false, - "hidden": false, - "tags": tags, - "icon": d.Icon, - } - - specBytes, err := json.Marshal(specMap) - if err != nil { - return reconcile.Result{}, err - } - - _, err = controllerutil.CreateOrUpdate(ctx, m.Client, mp, func() error { + _, err := controllerutil.CreateOrUpdate(ctx, m.Client, mp, func() error { if err := controllerutil.SetOwnerReference(crd, mp, m.Scheme); err != nil { return err } // Add dashboard labels to dynamic resources m.addDashboardLabels(mp, crd, ResourceTypeDynamic) + // Preserve user-set disabled/hidden values from existing resource + disabled := false + hidden := false + if mp.Spec.Raw != nil { + var existing map[string]any + if err := json.Unmarshal(mp.Spec.Raw, &existing); err == nil { + if v, ok := existing["disabled"].(bool); ok { + disabled = v + } + if v, ok := existing["hidden"].(bool); ok { + hidden = v + } + } + } + + specMap := map[string]any{ + "description": d.Description, + "name": displayName, + "type": "nonCrd", + "apiGroup": "apps.cozystack.io", + "apiVersion": "v1alpha1", + "plural": app.Plural, // e.g., "buckets" + "disabled": disabled, + "hidden": hidden, + "tags": tags, + "icon": d.Icon, + } + + specBytes, err := json.Marshal(specMap) + if err != nil { + return err + } + // Only update spec if it's different to avoid unnecessary updates newSpec := dashv1alpha1.ArbitrarySpec{ JSON: apiextv1.JSON{Raw: specBytes}, diff --git a/internal/controller/dashboard/sidebar.go b/internal/controller/dashboard/sidebar.go index b7a6eb70..69c933fb 100644 --- a/internal/controller/dashboard/sidebar.go +++ b/internal/controller/dashboard/sidebar.go @@ -17,8 +17,7 @@ import ( // ensureSidebar creates/updates multiple Sidebar resources that share the same menu: // - The "details" sidebar tied to the current kind (stock-project-factory--details) -// - The stock-instance sidebars: api-form, api-table, builtin-form, builtin-table -// - The stock-project sidebars: api-form, api-table, builtin-form, builtin-table, crd-form, crd-table +// - The stock-project sidebars: api-form, api-table, builtin-form, builtin-table, crd-form, crd-table // // Menu rules: // - The first section is "Marketplace" with two hardcoded entries: @@ -39,6 +38,23 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Applicati } all = crdList.Items + // 1b) Fetch all MarketplacePanels to determine which resources are hidden + hiddenResources := map[string]bool{} + var mpList dashv1alpha1.MarketplacePanelList + if err := m.List(ctx, &mpList, &client.ListOptions{}); err == nil { + for i := range mpList.Items { + mp := &mpList.Items[i] + if mp.Spec.Raw != nil { + var spec map[string]any + if err := json.Unmarshal(mp.Spec.Raw, &spec); err == nil { + if hidden, ok := spec["hidden"].(bool); ok && hidden { + hiddenResources[mp.Name] = true + } + } + } + } + } + // 2) Build category -> []item map (only for CRDs with spec.dashboard != nil) type item struct { Key string @@ -64,6 +80,11 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Applicati plural := pickPlural(kind, def) lowerKind := strings.ToLower(kind) + // Skip resources hidden via MarketplacePanel + if hiddenResources[def.Name] { + continue + } + // Check if this resource is a module if def.Spec.Dashboard.Module { // Special case: info should have its own keysAndTags, not be in modules @@ -123,6 +144,9 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Applicati // Add sidebar for backups.cozystack.io Backup resource keysAndTags["backups"] = []any{"backup-sidebar"} + // Add sidebar for backups.cozystack.io RestoreJob resource + keysAndTags["restorejobs"] = []any{"restorejob-sidebar"} + // 3) Sort items within each category by Weight (desc), then Label (A→Z) for cat := range categories { sort.Slice(categories[cat], func(i, j int) bool { @@ -176,23 +200,28 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Applicati // Add hardcoded Backups section menuItems = append(menuItems, map[string]any{ - "key": "backups", + "key": "backups-category", "label": "Backups", "children": []any{ map[string]any{ "key": "plans", "label": "Plans", - "link": "/openapi-ui/{clusterName}/{namespace}/api-table/backups.cozystack.io/v1alpha1/plans", + "link": "/openapi-ui/{cluster}/{namespace}/api-table/backups.cozystack.io/v1alpha1/plans", }, map[string]any{ "key": "backupjobs", "label": "BackupJobs", - "link": "/openapi-ui/{clusterName}/{namespace}/api-table/backups.cozystack.io/v1alpha1/backupjobs", + "link": "/openapi-ui/{cluster}/{namespace}/api-table/backups.cozystack.io/v1alpha1/backupjobs", }, map[string]any{ "key": "backups", "label": "Backups", - "link": "/openapi-ui/{clusterName}/{namespace}/api-table/backups.cozystack.io/v1alpha1/backups", + "link": "/openapi-ui/{cluster}/{namespace}/api-table/backups.cozystack.io/v1alpha1/backups", + }, + map[string]any{ + "key": "restorejobs", + "label": "RestoreJobs", + "link": "/openapi-ui/{cluster}/{namespace}/api-table/backups.cozystack.io/v1alpha1/restorejobs", }, }, }) @@ -215,7 +244,7 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Applicati map[string]any{ "key": "loadbalancer-services", "label": "External IPs", - "link": "/openapi-ui/{clusterName}/{namespace}/factory/external-ips", + "link": "/openapi-ui/{cluster}/{namespace}/factory/external-ips", }, map[string]any{ "key": "tenants", @@ -228,13 +257,7 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Applicati // 6) Prepare the list of Sidebar IDs to upsert with the SAME content // Create sidebars for ALL CRDs with dashboard config targetIDs := []string{ - // stock-instance sidebars - "stock-instance-api-form", - "stock-instance-api-table", - "stock-instance-builtin-form", - "stock-instance-builtin-table", - - // stock-project sidebars + // stock-project sidebars (namespace-level, full menu) "stock-project-factory-marketplace", "stock-project-factory-workloadmonitor-details", "stock-project-factory-kube-service-details", @@ -243,6 +266,7 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Applicati "stock-project-factory-plan-details", "stock-project-factory-backupjob-details", "stock-project-factory-backup-details", + "stock-project-factory-restorejob-details", "stock-project-factory-external-ips", "stock-project-api-form", "stock-project-api-table", @@ -250,9 +274,19 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Applicati "stock-project-builtin-table", "stock-project-crd-form", "stock-project-crd-table", + // stock-instance sidebars (namespace-level pages after namespace is selected) + "stock-instance-api-form", + "stock-instance-api-table", + "stock-instance-builtin-form", + "stock-instance-builtin-table", } - // Add details sidebars for all CRDs with dashboard config + // Add details sidebars for all CRDs with dashboard config, and collect + // the set of IDs that are genuinely CRD-backed (dynamic). The hardcoded + // `-details` IDs above (e.g. kube-* and backup/backupjob/plan/restorejob) + // are not tied to an ApplicationDefinition and must be treated as static + // so they receive consistent labels via upsertMultipleSidebars(). + dynamicDetailsIDs := map[string]bool{} for i := range all { def := &all[i] if def.Spec.Dashboard == nil { @@ -262,17 +296,22 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Applicati lowerKind := strings.ToLower(kind) detailsID := fmt.Sprintf("stock-project-factory-%s-details", lowerKind) targetIDs = append(targetIDs, detailsID) + dynamicDetailsIDs[detailsID] = true } // 7) Upsert all target sidebars with identical menuItems and keysAndTags - return m.upsertMultipleSidebars(ctx, crd, targetIDs, keysAndTags, menuItems) + return m.upsertMultipleSidebars(ctx, crd, targetIDs, dynamicDetailsIDs, keysAndTags, menuItems) } // upsertMultipleSidebars creates/updates several Sidebar resources with the same menu spec. +// dynamicDetailsIDs identifies `stock-project-factory--details` sidebars that are +// backed by an ApplicationDefinition and should therefore be owned by that CRD. +// Any other ID is treated as a static sidebar (managed-by labels, no owner ref). func (m *Manager) upsertMultipleSidebars( ctx context.Context, crd *cozyv1alpha1.ApplicationDefinition, ids []string, + dynamicDetailsIDs map[string]bool, keysAndTags map[string]any, menuItems []any, ) error { @@ -288,8 +327,10 @@ func (m *Manager) upsertMultipleSidebars( if _, err := controllerutil.CreateOrUpdate(ctx, m.Client, obj, func() error { // Only set owner reference for dynamic sidebars (stock-project-factory-{kind}-details) - // Static sidebars (stock-instance-*, stock-project-*) should not have owner references - if strings.HasPrefix(id, "stock-project-factory-") && strings.HasSuffix(id, "-details") { + // that are actually backed by an ApplicationDefinition. Static sidebars — including + // hardcoded details sidebars for built-in/backup resources — must fall through to the + // static-label branch so they're managed consistently. + if strings.HasPrefix(id, "stock-project-factory-") && strings.HasSuffix(id, "-details") && dynamicDetailsIDs[id] { // This is a dynamic sidebar, set owner reference only if it matches the current CRD _, _, kind := pickGVK(crd) lowerKind := strings.ToLower(kind) diff --git a/internal/controller/dashboard/static_helpers.go b/internal/controller/dashboard/static_helpers.go index 1a1018cb..db37e05b 100644 --- a/internal/controller/dashboard/static_helpers.go +++ b/internal/controller/dashboard/static_helpers.go @@ -650,12 +650,31 @@ func createStringColumn(name, jsonPath string) map[string]any { } } -// createTimestampColumn creates a timestamp column with custom formatting -func createTimestampColumn(name, jsonPath string) map[string]any { +// createTimestampColumn creates a timestamp column with custom formatting. +// Extra jsonPaths act as ordered fallbacks: the first non-null path wins, +// and `-` is used only when every path is null. Depth is capped at two +// because the template parser's non-greedy matcher cannot track more than +// one level of alternating quotes in a nested reqsJsonPath fallback. +func createTimestampColumn(name string, jsonPaths ...string) map[string]any { + if len(jsonPaths) == 0 { + panic("createTimestampColumn requires at least one jsonPath") + } + if len(jsonPaths) > 2 { + panic("createTimestampColumn supports at most two jsonPaths (primary + one fallback)") + } + + var text string + switch len(jsonPaths) { + case 1: + text = "{reqsJsonPath[0]['" + jsonPaths[0] + "']['-']}" + case 2: + text = "{reqsJsonPath[0]['" + jsonPaths[0] + "'][\"{reqsJsonPath[0]['" + jsonPaths[1] + "']['-']}\"]}" + } + return map[string]any{ "name": name, "type": "factory", - "jsonPath": jsonPath, + "jsonPath": jsonPaths[0], "customProps": map[string]any{ "disableEventBubbling": true, "items": []any{ @@ -673,7 +692,7 @@ func createTimestampColumn(name, jsonPath string) map[string]any { "data": map[string]any{ "formatter": "timestamp", "id": "time-value", - "text": "{reqsJsonPath[0]['" + jsonPath + "']['-']}", + "text": text, }, }, }, diff --git a/internal/controller/dashboard/static_refactored.go b/internal/controller/dashboard/static_refactored.go index d47c88da..9f28c0fb 100644 --- a/internal/controller/dashboard/static_refactored.go +++ b/internal/controller/dashboard/static_refactored.go @@ -154,6 +154,14 @@ func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverrid createStringColumn("Version", ".status.version"), }), + // Factory service details endpointslice (Pod serving table) + createCustomColumnsOverride("factory-kube-service-details-endpointslice", []any{ + createStringColumn("Pod", ".targetRef.name"), + createArrayColumn("Addresses", ".addresses"), + createBoolColumn("Ready", ".conditions.ready"), + createStringColumn("Node", ".nodeName"), + }), + // Factory service details port mapping createCustomColumnsOverride("factory-kube-service-details-port-mapping", []any{ createStringColumn("Name", ".name"), @@ -216,6 +224,20 @@ func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverrid createStringColumn("Name", ".name"), }), + // Factory details events. Event Time falls back to `.firstTimestamp` + // because core/v1 Events (Helm, controller-runtime) populate only the + // legacy timestamps while events.k8s.io/v1 Events populate only + // `.eventTime`; taking the first non-null of the two covers both. + createCustomColumnsOverride("factory-details-events", []any{ + createTimestampColumn("Last Seen", ".lastTimestamp", ".eventTime"), + createTimestampColumn("Event Time", ".eventTime", ".firstTimestamp"), + createStringColumn("Type", ".type"), + createStringColumn("Reason", ".reason"), + createStringColumn("Object", ".involvedObject.kind"), + createStringColumn("Name", ".involvedObject.name"), + createStringColumn("Message", ".message"), + }), + // Factory status conditions createCustomColumnsOverride("factory-status-conditions", []any{ createStringColumn("Type", ".type"), @@ -403,6 +425,14 @@ func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverrid createTimestampColumn("Taken At", ".spec.takenAt"), createTimestampColumn("Created", ".metadata.creationTimestamp"), }), + + // Stock namespace backups cozystack io v1alpha1 restorejobs + createCustomColumnsOverride("stock-namespace-/backups.cozystack.io/v1alpha1/restorejobs", []any{ + createCustomColumnWithJsonPath("Name", ".metadata.name", "RestoreJob", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/restorejob-details/{reqsJsonPath[0]['.metadata.name']['-']}"), + createStringColumn("Phase", ".status.phase"), + createStringColumn("Backup", ".spec.backupRef.name"), + createTimestampColumn("Created", ".metadata.creationTimestamp"), + }), } } @@ -503,18 +533,52 @@ func CreateAllCustomFormsOverrides() []*dashboardv1alpha1.CustomFormsOverride { createFormItem("metadata.namespace", "Namespace", "text"), createFormItem("spec.applicationRef.kind", "Application Kind", "text"), createFormItem("spec.applicationRef.name", "Application Name", "text"), - createFormItemWithAPI("spec.backupClassName", "Backup Class", "select", map[string]any{ - "api": map[string]any{ - "fetchUrl": "/api/clusters/{clusterName}/k8s/apis/backups.cozystack.io/v1alpha1/backupclasses", - "pathToItems": []any{"items"}, - "pathToValue": []any{"metadata", "name"}, - "pathToLabel": []any{"metadata", "name"}, - "clusterNameVar": "clusterName", - }, - }), createFormItem("spec.schedule.type", "Schedule Type", "text"), createFormItem("spec.schedule.cron", "Schedule Cron", "text"), }, + "schema": createSchema(map[string]any{ + "backupClassName": listInputScemaItemBackupClass(), + }), + }), + + // BackupJobs form override - backups.cozystack.io/v1alpha1 + createCustomFormsOverride("default-/backups.cozystack.io/v1alpha1/backupjobs", map[string]any{ + "formItems": []any{ + createFormItem("metadata.name", "Name", "text"), + createFormItem("metadata.namespace", "Namespace", "text"), + createFormItem("spec.planRef.name", "Plan Name (optional)", "text"), + createFormItem("spec.applicationRef.apiGroup", "Application API Group", "text"), + createFormItem("spec.applicationRef.kind", "Application Kind", "text"), + createFormItem("spec.applicationRef.name", "Application Name", "text"), + }, + "schema": createSchema(map[string]any{ + "backupClassName": listInputScemaItemBackupClass(), + }), + }), + + // RestoreJobs form override - backups.cozystack.io/v1alpha1 + createCustomFormsOverride("default-/backups.cozystack.io/v1alpha1/restorejobs", map[string]any{ + "formItems": []any{ + createFormItem("metadata.name", "Name", "text"), + createFormItem("metadata.namespace", "Namespace", "text"), + createFormItem("spec.backupRef.name", "Backup", "text"), + // Target application: leave empty to restore into the same application + // as referenced by the selected Backup. Fill all three to restore into + // a different application (e.g. rename, or restore into a new target). + createFormItem("spec.targetApplicationRef.apiGroup", "Target Application API Group (optional, used to restore into a different application)", "text"), + createFormItem("spec.targetApplicationRef.kind", "Target Application Kind (optional, used to restore into a different application)", "text"), + createFormItem("spec.targetApplicationRef.name", "Target Application Name (optional, used to restore into a different application)", "text"), + // Driver-specific options (key-value editor). Refer to the backup driver + // documentation for supported keys. + createFormItem("spec.options", "Options (driver-specific key/value pairs)", "object"), + }, + "schema": createSchema(map[string]any{ + "backupRef": map[string]any{ + "properties": map[string]any{ + "name": listInputSchemaItemBackup(), + }, + }, + }), }), } } @@ -1906,6 +1970,177 @@ func CreateAllFactories() []*dashboardv1alpha1.Factory { } backupSpec := createUnifiedFactory(backupConfig, backupTabs, []any{"/api/clusters/{2}/k8s/apis/backups.cozystack.io/v1alpha1/namespaces/{3}/backups/{6}"}) + // RestoreJob details factory using unified approach + restoreJobConfig := UnifiedResourceConfig{ + Name: "restorejob-details", + ResourceType: "factory", + Kind: "RestoreJob", + Plural: "restorejobs", + Title: "restorejob", + } + restoreJobTabs := []any{ + map[string]any{ + "key": "details", + "label": "Details", + "children": []any{ + contentCard("details-card", map[string]any{ + "marginBottom": "24px", + }, []any{ + antdText("details-title", true, "RestoreJob details", map[string]any{ + "fontSize": 20, + "marginBottom": "12px", + }), + spacer("details-spacer", 16), + antdRow("details-grid", []any{48, 12}, []any{ + antdCol("col-left", 12, []any{ + antdFlexVertical("col-left-stack", 24, []any{ + antdFlexVertical("meta-name-block", 4, []any{ + antdText("meta-name-label", true, "Name", nil), + parsedText("meta-name-value", "{reqsJsonPath[0]['.metadata.name']['-']}", nil), + }), + antdFlexVertical("meta-namespace-block", 8, []any{ + antdText("meta-namespace-label", true, "Namespace", nil), + antdFlex("header-row", 6, []any{ + map[string]any{ + "type": "antdText", + "data": map[string]any{ + "id": "header-badge", + "text": "NS", + "title": "namespace", + "style": map[string]any{ + "backgroundColor": "#a25792ff", + "borderRadius": "20px", + "color": "#fff", + "display": "inline-block", + "fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif", + "fontSize": "15px", + "fontWeight": 400, + "lineHeight": "24px", + "minWidth": 24, + "padding": "0 9px", + "textAlign": "center", + "whiteSpace": "nowrap", + }, + }, + }, + map[string]any{ + "type": "antdLink", + "data": map[string]any{ + "id": "namespace-link", + "text": "{reqsJsonPath[0]['.metadata.namespace']['-']}", + "href": "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace", + }, + }, + }), + }), + antdFlexVertical("meta-created-block", 4, []any{ + antdText("created-time-label", true, "Created", nil), + antdFlex("created-time-block", 6, []any{ + map[string]any{ + "type": "antdText", + "data": map[string]any{ + "id": "created-time-icon", + "text": "🌐", + }, + }, + map[string]any{ + "type": "parsedText", + "data": map[string]any{ + "formatter": "timestamp", + "id": "created-time-value", + "text": "{reqsJsonPath[0]['.metadata.creationTimestamp']['-']}", + }, + }, + }), + }), + }), + }), + antdCol("col-right", 12, []any{ + antdFlexVertical("col-right-stack", 24, []any{ + antdFlexVertical("status-phase-block", 4, []any{ + antdText("phase-label", true, "Phase", nil), + parsedText("phase-value", "{reqsJsonPath[0]['.status.phase']['-']}", nil), + }), + antdFlexVertical("spec-backup-ref-block", 4, []any{ + antdText("backup-ref-label", true, "Backup Ref", nil), + parsedText("backup-ref-value", "{reqsJsonPath[0]['.spec.backupRef.name']['-']}", nil), + }), + antdFlexVertical("spec-target-application-ref-block", 4, []any{ + antdText("target-application-ref-label", true, "Target Application", nil), + // targetApplicationRef is optional — when absent, the restore targets + // the same application as the selected Backup. Show a single friendly + // fallback in that case instead of rendering "-.-/-". + parsedText("target-application-ref-value", "{reqsJsonPath[0]['.spec.targetApplicationRef.name']['Same as backup']}", nil), + }), + antdFlexVertical("spec-options-target-namespace-block", 4, []any{ + antdText("options-target-namespace-label", true, "Target Namespace", nil), + parsedText("options-target-namespace-value", "{reqsJsonPath[0]['.spec.options.targetNamespace']['-']}", nil), + }), + antdFlexVertical("spec-options-fail-if-target-exists-block", 4, []any{ + antdText("options-fail-if-target-exists-label", true, "Fail If Target Exists", nil), + parsedText("options-fail-if-target-exists-value", "{reqsJsonPath[0]['.spec.options.failIfTargetExists']['-']}", nil), + }), + antdFlexVertical("spec-options-keep-original-pvc-block", 4, []any{ + antdText("options-keep-original-pvc-label", true, "Keep Original PVC", nil), + parsedText("options-keep-original-pvc-value", "{reqsJsonPath[0]['.spec.options.keepOriginalPVC']['-']}", nil), + }), + antdFlexVertical("spec-options-keep-original-ip-mac-block", 4, []any{ + antdText("options-keep-original-ip-mac-label", true, "Keep Original IP/MAC", nil), + parsedText("options-keep-original-ip-mac-value", "{reqsJsonPath[0]['.spec.options.keepOriginalIpAndMac']['-']}", nil), + }), + antdFlexVertical("status-started-at-block", 4, []any{ + antdText("started-at-label", true, "Started At", nil), + antdFlex("started-at-time-block", 6, []any{ + map[string]any{ + "type": "antdText", + "data": map[string]any{ + "id": "started-at-time-icon", + "text": "🌐", + }, + }, + map[string]any{ + "type": "parsedText", + "data": map[string]any{ + "formatter": "timestamp", + "id": "started-at-time-value", + "text": "{reqsJsonPath[0]['.status.startedAt']['-']}", + }, + }, + }), + }), + antdFlexVertical("status-completed-at-block", 4, []any{ + antdText("completed-at-label", true, "Completed At", nil), + antdFlex("completed-at-time-block", 6, []any{ + map[string]any{ + "type": "antdText", + "data": map[string]any{ + "id": "completed-at-time-icon", + "text": "🌐", + }, + }, + map[string]any{ + "type": "parsedText", + "data": map[string]any{ + "formatter": "timestamp", + "id": "completed-at-time-value", + "text": "{reqsJsonPath[0]['.status.completedAt']['-']}", + }, + }, + }), + }), + antdFlexVertical("status-message-block", 4, []any{ + antdText("message-label", true, "Message", nil), + parsedText("message-value", "{reqsJsonPath[0]['.status.message']['-']}", nil), + }), + }), + }), + }), + }), + }, + }, + } + restoreJobSpec := createUnifiedFactory(restoreJobConfig, restoreJobTabs, []any{"/api/clusters/{2}/k8s/apis/backups.cozystack.io/v1alpha1/namespaces/{3}/restorejobs/{6}"}) + // External IPs factory (filtered services) externalIPsTabs := []any{ map[string]any{ @@ -1915,12 +2150,12 @@ func CreateAllFactories() []*dashboardv1alpha1.Factory { map[string]any{ "type": "EnrichedTable", "data": map[string]any{ - "id": "external-ips-table", - "fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/services", - "clusterNamePartOfUrl": "{2}", - "baseprefix": "/openapi-ui", - "customizationId": "factory-details-v1.services", - "pathToItems": []any{"items"}, + "id": "external-ips-table", + "fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/services", + "cluster": "{2}", + "baseprefix": "/openapi-ui", + "customizationId": "factory-details-v1.services", + "pathToItems": ".items", "fieldSelector": map[string]any{ "spec.type": "LoadBalancer", }, @@ -1958,6 +2193,7 @@ func CreateAllFactories() []*dashboardv1alpha1.Factory { createFactory("plan-details", planSpec), createFactory("backupjob-details", backupJobSpec), createFactory("backup-details", backupSpec), + createFactory("restorejob-details", restoreJobSpec), createFactory("external-ips", externalIPsSpec), } } @@ -1976,6 +2212,11 @@ func CreateAllNavigations() []*dashboardv1alpha1.Navigation { // Namespaced API resources "base-factory-namespaced-api-networking.k8s.io-v1-ingresses": "kube-ingress-details", "base-factory-namespaced-api-cozystack.io-v1alpha1-workloadmonitors": "workloadmonitor-details", + // Backup resources (not ApplicationDefinitions, so ensureNavigation doesn't cover them) + "base-factory-namespaced-api-backups.cozystack.io-v1alpha1-plans": "plan-details", + "base-factory-namespaced-api-backups.cozystack.io-v1alpha1-backupjobs": "backupjob-details", + "base-factory-namespaced-api-backups.cozystack.io-v1alpha1-backups": "backup-details", + "base-factory-namespaced-api-backups.cozystack.io-v1alpha1-restorejobs": "restorejob-details", } return []*dashboardv1alpha1.Navigation{ @@ -2042,9 +2283,9 @@ func createCustomFormsOverride(customizationId string, spec map[string]any) *das "strategy": "merge", } - // Merge caller-provided fields (like formItems) into newSpec + // Merge into newSpec caller-provided fields without: customizationId, hidden, strategy for key, value := range spec { - if key != "customizationId" && key != "hidden" && key != "schema" && key != "strategy" { + if key != "customizationId" && key != "hidden" && key != "strategy" { newSpec[key] = value } } @@ -2089,6 +2330,41 @@ func createNavigation(name string, spec map[string]any) *dashboardv1alpha1.Navig } } +func listInputScemaItemBackupClass() map[string]any { + return map[string]any{ + "type": "listInput", + "customProps": map[string]any{ + "valueUri": "/api/clusters/{cluster}/k8s/apis/backups.cozystack.io/v1alpha1/backupclasses", + "keysToValue": []any{"metadata", "name"}, + "keysToLabel": []any{"metadata", "name"}, + }, + } +} + +// listInputSchemaItemBackup returns a listInput schema overlay for selecting a Backup +// from the current namespace (used by RestoreJob form for spec.backupRef.name). +func listInputSchemaItemBackup() map[string]any { + return map[string]any{ + "type": "listInput", + "customProps": map[string]any{ + "valueUri": "/api/clusters/{cluster}/k8s/apis/backups.cozystack.io/v1alpha1/namespaces/{namespace}/backups", + "keysToValue": []any{"metadata", "name"}, + "keysToLabel": []any{"metadata", "name"}, + }, + } +} + +// backupClassSchema returns the schema for spec.backupClassName as listInput (BackupJob/Plan). +func createSchema(customProps map[string]any) map[string]any { + return map[string]any{ + "properties": map[string]any{ + "spec": map[string]any{ + "properties": customProps, + }, + }, + } +} + // createFormItem creates a form item for CustomFormsOverride func createFormItem(path, label, fieldType string) map[string]any { return map[string]any{ diff --git a/internal/controller/workloadmonitor_controller.go b/internal/controller/workloadmonitor_controller.go index a684df9b..35906a87 100644 --- a/internal/controller/workloadmonitor_controller.go +++ b/internal/controller/workloadmonitor_controller.go @@ -4,7 +4,13 @@ import ( "context" "encoding/json" "fmt" + "io" + "net/http" + "net/url" "sort" + "strconv" + "strings" + "time" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -22,8 +28,29 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1" + cosiv1alpha1 "sigs.k8s.io/container-object-storage-interface-api/apis/objectstorage/v1alpha1" ) +const ( + // namespaceMonitoringLabel is the namespace label that indicates which tenant + // namespace hosts the monitoring stack (VictoriaMetrics/Prometheus). + namespaceMonitoringLabel = "namespace.cozystack.io/monitoring" + workloadLabelPrefix = "workloads.cozystack.io/" + // workloadMonitorLabel is reserved: it names the WorkloadMonitor that owns + // the Workload and is always set by the reconciler, so it is never copied + // from monitor labels. + workloadMonitorLabel = workloadLabelPrefix + "monitor" + // vmSelectService is the well-known service name for VictoriaMetrics vmselect + // within a monitoring namespace. Port 8481, path /select/0/prometheus. + vmSelectService = "vmselect-shortterm" + vmSelectPort = "8481" + vmSelectPath = "/select/0/prometheus" +) + +// prometheusHTTPClient is a dedicated HTTP client for Prometheus queries, +// avoiding the shared http.DefaultClient global. +var prometheusHTTPClient = &http.Client{Timeout: 10 * time.Second} + // WorkloadMonitorReconciler reconciles a WorkloadMonitor object type WorkloadMonitorReconciler struct { client.Client @@ -36,6 +63,13 @@ type WorkloadMonitorReconciler struct { // +kubebuilder:rbac:groups=cozystack.io,resources=workloads/status,verbs=get;update;patch // +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch // +kubebuilder:rbac:groups=core,resources=persistentvolumeclaims,verbs=get;list;watch +// +kubebuilder:rbac:groups=core,resources=namespaces,verbs=get +// +kubebuilder:rbac:groups=objectstorage.k8s.io,resources=bucketclaims,verbs=get;list;watch + +// isBucketClaimReady checks if the BucketClaim has been provisioned. +func (r *WorkloadMonitorReconciler) isBucketClaimReady(bc *cosiv1alpha1.BucketClaim) bool { + return bc.Status.BucketReady +} // isServiceReady checks if the service has an external IP bound func (r *WorkloadMonitorReconciler) isServiceReady(svc *corev1.Service) bool { @@ -101,6 +135,190 @@ func updateOwnerReferences(obj metav1.Object, monitor client.Object) { obj.SetOwnerReferences(owners) } +// resolvePrometheusURL returns the Prometheus-compatible API base URL for the given namespace. +// It reads the namespace.cozystack.io/monitoring label to find the monitoring namespace, +// then constructs the vmselect URL. Returns empty string if monitoring is not configured. +func (r *WorkloadMonitorReconciler) resolvePrometheusURL(ctx context.Context, namespace string) string { + logger := log.FromContext(ctx) + ns := &corev1.Namespace{} + if err := r.Get(ctx, types.NamespacedName{Name: namespace}, ns); err != nil { + logger.V(1).Info("Failed to read namespace for monitoring resolution", "namespace", namespace, "error", err) + return "" + } + monitoringNS := ns.Labels[namespaceMonitoringLabel] + if monitoringNS == "" { + return "" + } + return fmt.Sprintf("http://%s.%s.svc:%s%s", vmSelectService, monitoringNS, vmSelectPort, vmSelectPath) +} + +// bucketMetrics holds size metrics for a single bucket, keyed by metric name. +type bucketMetrics struct { + LogicalSize int64 + PhysicalSize int64 + HasLogical bool + HasPhysical bool +} + +// queryAllBucketMetrics fetches SeaweedFS bucket size metrics for the given +// bucket names in a single Prometheus query and returns them keyed by bucket +// name. The query is scoped to only the requested buckets to avoid fetching +// metrics for buckets belonging to other WorkloadMonitors. +func (r *WorkloadMonitorReconciler) queryAllBucketMetrics(ctx context.Context, prometheusBaseURL string, bucketNames []string) map[string]*bucketMetrics { + result := make(map[string]*bucketMetrics) + if prometheusBaseURL == "" || len(bucketNames) == 0 { + return result + } + logger := log.FromContext(ctx) + + query := fmt.Sprintf(`{__name__=~"SeaweedFS_s3_bucket_(size|physical_size)_bytes",bucket=~"%s"}`, strings.Join(bucketNames, "|")) + u, err := url.Parse(strings.TrimRight(prometheusBaseURL, "/") + "/api/v1/query") + if err != nil { + logger.Error(err, "Failed to parse Prometheus URL") + return result + } + u.RawQuery = url.Values{"query": {query}}.Encode() + + httpCtx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + + req, err := http.NewRequestWithContext(httpCtx, http.MethodGet, u.String(), nil) + if err != nil { + logger.Error(err, "Failed to create Prometheus request") + return result + } + + resp, err := prometheusHTTPClient.Do(req) + if err != nil { + logger.V(1).Info("Failed to query Prometheus for bucket metrics", "error", err) + return result + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + logger.V(1).Info("Prometheus returned non-OK status for bucket metrics", "status", resp.StatusCode) + return result + } + + body, err := io.ReadAll(io.LimitReader(resp.Body, 4<<20)) + if err != nil { + logger.Error(err, "Failed to read Prometheus response") + return result + } + + var promResp struct { + Status string `json:"status"` + Data struct { + Result []struct { + Metric map[string]string `json:"metric"` + Value [2]json.RawMessage `json:"value"` + } `json:"result"` + } `json:"data"` + } + if err := json.Unmarshal(body, &promResp); err != nil { + logger.Error(err, "Failed to parse Prometheus response") + return result + } + if promResp.Status != "success" { + return result + } + + for _, r := range promResp.Data.Result { + bucket := r.Metric["bucket"] + metricName := r.Metric["__name__"] + if bucket == "" || metricName == "" { + continue + } + + var valueStr string + if err := json.Unmarshal(r.Value[1], &valueStr); err != nil { + continue + } + val, err := strconv.ParseFloat(valueStr, 64) + if err != nil { + continue + } + + bm, ok := result[bucket] + if !ok { + bm = &bucketMetrics{} + result[bucket] = bm + } + + switch metricName { + case "SeaweedFS_s3_bucket_size_bytes": + bm.LogicalSize = int64(val) + bm.HasLogical = true + case "SeaweedFS_s3_bucket_physical_size_bytes": + bm.PhysicalSize = int64(val) + bm.HasPhysical = true + } + } + + return result +} + +// reconcileBucketClaimForMonitor creates or updates a Workload object for the given BucketClaim and WorkloadMonitor. +func (r *WorkloadMonitorReconciler) reconcileBucketClaimForMonitor( + ctx context.Context, + monitor *cozyv1alpha1.WorkloadMonitor, + bc cosiv1alpha1.BucketClaim, + allMetrics map[string]*bucketMetrics, +) error { + logger := log.FromContext(ctx) + workload := &cozyv1alpha1.Workload{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("bucket-%s", bc.Name), + Namespace: bc.Namespace, + Labels: make(map[string]string, len(bc.Labels)), + }, + } + + resources := make(map[string]resource.Quantity) + + // Look up pre-fetched bucket metrics by the SeaweedFS bucket name. + // bc.Status.BucketName is the COSI Bucket name, which the COSI driver + // uses directly as the SeaweedFS bucket name. + if bm, ok := allMetrics[bc.Status.BucketName]; ok { + if bm.HasLogical { + resources["s3-storage-bytes"] = *resource.NewQuantity(bm.LogicalSize, resource.BinarySI) + } + if bm.HasPhysical { + resources["s3-physical-storage-bytes"] = *resource.NewQuantity(bm.PhysicalSize, resource.BinarySI) + } + } + + monitorLabels := r.getMonitorLabels(monitor) + _, err := ctrl.CreateOrUpdate(ctx, r.Client, workload, func() error { + updateOwnerReferences(workload.GetObjectMeta(), &bc) + + if workload.Labels == nil { + workload.Labels = make(map[string]string) + } + // Apply monitor-level labels first so source-object labels can override on conflict + for k, v := range monitorLabels { + workload.Labels[k] = v + } + for k, v := range bc.Labels { + workload.Labels[k] = v + } + workload.Labels[workloadMonitorLabel] = monitor.Name + + workload.Status.Kind = monitor.Spec.Kind + workload.Status.Type = monitor.Spec.Type + workload.Status.Resources = resources + workload.Status.Operational = r.isBucketClaimReady(&bc) + + return nil + }) + if err != nil { + logger.Error(err, "Failed to CreateOrUpdate Workload", "workload", workload.Name) + return err + } + + return nil +} + // reconcileServiceForMonitor creates or updates a Workload object for the given Service and WorkloadMonitor. func (r *WorkloadMonitorReconciler) reconcileServiceForMonitor( ctx context.Context, @@ -137,14 +355,22 @@ func (r *WorkloadMonitorReconciler) reconcileServiceForMonitor( resourceLabel = fmt.Sprintf("%s.ipaddresspool.metallb.io/requests.ipaddresses", resourceLabel) resources[resourceLabel] = quantity + monitorLabels := r.getMonitorLabels(monitor) _, err := ctrl.CreateOrUpdate(ctx, r.Client, workload, func() error { // Update owner references with the new monitor updateOwnerReferences(workload.GetObjectMeta(), &svc) + // Apply monitor-level labels first so source-object labels can override on conflict + if workload.Labels == nil { + workload.Labels = make(map[string]string) + } + for k, v := range monitorLabels { + workload.Labels[k] = v + } for k, v := range svc.Labels { workload.Labels[k] = v } - workload.Labels["workloads.cozystack.io/monitor"] = monitor.Name + workload.Labels[workloadMonitorLabel] = monitor.Name // Fill Workload status fields: workload.Status.Kind = monitor.Spec.Kind @@ -188,14 +414,22 @@ func (r *WorkloadMonitorReconciler) reconcilePVCForMonitor( resources[resourceLabel] = resourceQuantity } + monitorLabels := r.getMonitorLabels(monitor) _, err := ctrl.CreateOrUpdate(ctx, r.Client, workload, func() error { // Update owner references with the new monitor updateOwnerReferences(workload.GetObjectMeta(), &pvc) + // Apply monitor-level labels first so source-object labels can override on conflict + if workload.Labels == nil { + workload.Labels = make(map[string]string) + } + for k, v := range monitorLabels { + workload.Labels[k] = v + } for k, v := range pvc.Labels { workload.Labels[k] = v } - workload.Labels["workloads.cozystack.io/monitor"] = monitor.Name + workload.Labels[workloadMonitorLabel] = monitor.Name // Fill Workload status fields: workload.Status.Kind = monitor.Spec.Kind @@ -262,14 +496,22 @@ func (r *WorkloadMonitorReconciler) reconcilePodForMonitor( } metaLabels := r.getWorkloadMetadata(&pod) + monitorLabels := r.getMonitorLabels(monitor) _, err := ctrl.CreateOrUpdate(ctx, r.Client, workload, func() error { // Update owner references with the new monitor updateOwnerReferences(workload.GetObjectMeta(), &pod) + // Apply monitor-level labels first so source-object labels can override on conflict + if workload.Labels == nil { + workload.Labels = make(map[string]string) + } + for k, v := range monitorLabels { + workload.Labels[k] = v + } for k, v := range pod.Labels { workload.Labels[k] = v } - workload.Labels["workloads.cozystack.io/monitor"] = monitor.Name + workload.Labels[workloadMonitorLabel] = monitor.Name // Add workload meta to labels for k, v := range metaLabels { @@ -375,6 +617,34 @@ func (r *WorkloadMonitorReconciler) Reconcile(ctx context.Context, req ctrl.Requ } } + bucketClaimList := &cosiv1alpha1.BucketClaimList{} + if err := r.List( + ctx, + bucketClaimList, + client.InNamespace(monitor.Namespace), + client.MatchingLabels(monitor.Spec.Selector), + ); err != nil { + logger.Error(err, "Unable to list BucketClaims for WorkloadMonitor", "monitor", monitor.Name) + return ctrl.Result{}, err + } + + if len(bucketClaimList.Items) > 0 { + bucketPromURL := r.resolvePrometheusURL(ctx, monitor.Namespace) + var bucketNames []string + for _, bc := range bucketClaimList.Items { + if bc.Status.BucketName != "" { + bucketNames = append(bucketNames, bc.Status.BucketName) + } + } + allBucketMetrics := r.queryAllBucketMetrics(ctx, bucketPromURL, bucketNames) + for _, bc := range bucketClaimList.Items { + if err := r.reconcileBucketClaimForMonitor(ctx, monitor, bc, allBucketMetrics); err != nil { + logger.Error(err, "Failed to reconcile Workload for BucketClaim", "BucketClaim", bc.Name) + continue + } + } + } + // Update WorkloadMonitor status based on observed pods monitor.Status.ObservedReplicas = observedReplicas monitor.Status.AvailableReplicas = availableReplicas @@ -388,10 +658,12 @@ func (r *WorkloadMonitorReconciler) Reconcile(ctx context.Context, req ctrl.Requ fresh.Status.ObservedReplicas = observedReplicas fresh.Status.AvailableReplicas = availableReplicas - // Default to operational = true, but check MinReplicas if set - monitor.Status.Operational = pointer.Bool(true) - if monitor.Spec.MinReplicas != nil && availableReplicas < *monitor.Spec.MinReplicas { - monitor.Status.Operational = pointer.Bool(false) + // Default to operational = true, but check MinReplicas if set. + // Use fresh.Spec to avoid making decisions based on a stale cached copy + // when the spec was updated between the initial read and this retry. + fresh.Status.Operational = pointer.Bool(true) + if fresh.Spec.MinReplicas != nil && availableReplicas < *fresh.Spec.MinReplicas { + fresh.Status.Operational = pointer.Bool(false) } return r.Status().Update(ctx, fresh) }) @@ -400,7 +672,12 @@ func (r *WorkloadMonitorReconciler) Reconcile(ctx context.Context, req ctrl.Requ return ctrl.Result{}, err } - // Return without requeue if we want purely event-driven reconciliations + // Requeue periodically if there are BucketClaims to keep sizes up to date. + // Bucket sizes come from Prometheus metrics that update every 60s. + if len(bucketClaimList.Items) > 0 { + return ctrl.Result{RequeueAfter: 60 * time.Second}, nil + } + return ctrl.Result{}, nil } @@ -419,6 +696,11 @@ func (r *WorkloadMonitorReconciler) SetupWithManager(mgr ctrl.Manager) error { &corev1.PersistentVolumeClaim{}, handler.EnqueueRequestsFromMapFunc(mapObjectToMonitor(&corev1.PersistentVolumeClaim{}, r.Client)), ). + // Watch BucketClaims for S3 bucket billing + Watches( + &cosiv1alpha1.BucketClaim{}, + handler.EnqueueRequestsFromMapFunc(mapObjectToMonitor(&cosiv1alpha1.BucketClaim{}, r.Client)), + ). // Watch for changes to Workload objects we create (owned by WorkloadMonitor) Owns(&cozyv1alpha1.Workload{}). Complete(r) @@ -472,3 +754,21 @@ func (r *WorkloadMonitorReconciler) getWorkloadMetadata(obj client.Object) map[s } return labels } + +// getMonitorLabels extracts workloads.cozystack.io/* labels from a WorkloadMonitor +// so they can be propagated onto Workload objects created for pods, PVCs, services, +// or bucket claims. The monitor label "workloads.cozystack.io/monitor" is reserved +// and set separately per Workload, so it is excluded here. +func (r *WorkloadMonitorReconciler) getMonitorLabels(monitor *cozyv1alpha1.WorkloadMonitor) map[string]string { + labels := make(map[string]string) + for k, v := range monitor.GetLabels() { + if !strings.HasPrefix(k, workloadLabelPrefix) { + continue + } + if k == workloadMonitorLabel { + continue + } + labels[k] = v + } + return labels +} diff --git a/internal/controller/workloadmonitor_controller_test.go b/internal/controller/workloadmonitor_controller_test.go new file mode 100644 index 00000000..355b0379 --- /dev/null +++ b/internal/controller/workloadmonitor_controller_test.go @@ -0,0 +1,846 @@ +package controller + +import ( + "context" + "fmt" + "net/http" + "net/http/httptest" + "strings" + "testing" + + cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + cosiv1alpha1 "sigs.k8s.io/container-object-storage-interface-api/apis/objectstorage/v1alpha1" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +func TestReconcile_OperationalStatusPersisted(t *testing.T) { + scheme := runtime.NewScheme() + _ = cozyv1alpha1.AddToScheme(scheme) + _ = corev1.AddToScheme(scheme) + _ = cosiv1alpha1.AddToScheme(scheme) + + minReplicas := int32(2) + monitor := &cozyv1alpha1.WorkloadMonitor{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-monitor", + Namespace: "default", + }, + Spec: cozyv1alpha1.WorkloadMonitorSpec{ + Selector: map[string]string{"app": "test"}, + MinReplicas: &minReplicas, + }, + } + + // Create one pod that is ready — availableReplicas=1 < minReplicas=2, so Operational should be false + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-pod-1", + Namespace: "default", + Labels: map[string]string{"app": "test"}, + }, + Status: corev1.PodStatus{ + Conditions: []corev1.PodCondition{ + {Type: corev1.PodReady, Status: corev1.ConditionTrue}, + }, + }, + } + + fakeClient := fake.NewClientBuilder(). + WithScheme(scheme). + WithObjects(monitor, pod). + WithStatusSubresource(monitor). + Build() + + reconciler := &WorkloadMonitorReconciler{Client: fakeClient, Scheme: scheme} + req := reconcile.Request{NamespacedName: types.NamespacedName{Name: "test-monitor", Namespace: "default"}} + + _, err := reconciler.Reconcile(context.TODO(), req) + if err != nil { + t.Fatalf("Reconcile returned error: %v", err) + } + + // Fetch the monitor back from fake client and check Operational is persisted + updated := &cozyv1alpha1.WorkloadMonitor{} + if err := fakeClient.Get(context.TODO(), req.NamespacedName, updated); err != nil { + t.Fatalf("Failed to get updated WorkloadMonitor: %v", err) + } + + if updated.Status.Operational == nil { + t.Fatal("Expected Operational to be set, got nil") + } + if *updated.Status.Operational { + t.Error("Expected Operational=false (1 available < 2 minReplicas), got true") + } + if updated.Status.ObservedReplicas != 1 { + t.Errorf("Expected ObservedReplicas=1, got %d", updated.Status.ObservedReplicas) + } + if updated.Status.AvailableReplicas != 1 { + t.Errorf("Expected AvailableReplicas=1, got %d", updated.Status.AvailableReplicas) + } +} + +func TestReconcile_OperationalTrue_WhenEnoughReplicas(t *testing.T) { + scheme := runtime.NewScheme() + _ = cozyv1alpha1.AddToScheme(scheme) + _ = corev1.AddToScheme(scheme) + _ = cosiv1alpha1.AddToScheme(scheme) + + minReplicas := int32(1) + monitor := &cozyv1alpha1.WorkloadMonitor{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-monitor", + Namespace: "default", + }, + Spec: cozyv1alpha1.WorkloadMonitorSpec{ + Selector: map[string]string{"app": "test"}, + MinReplicas: &minReplicas, + }, + } + + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-pod-1", + Namespace: "default", + Labels: map[string]string{"app": "test"}, + }, + Status: corev1.PodStatus{ + Conditions: []corev1.PodCondition{ + {Type: corev1.PodReady, Status: corev1.ConditionTrue}, + }, + }, + } + + fakeClient := fake.NewClientBuilder(). + WithScheme(scheme). + WithObjects(monitor, pod). + WithStatusSubresource(monitor). + Build() + + reconciler := &WorkloadMonitorReconciler{Client: fakeClient, Scheme: scheme} + req := reconcile.Request{NamespacedName: types.NamespacedName{Name: "test-monitor", Namespace: "default"}} + + _, err := reconciler.Reconcile(context.TODO(), req) + if err != nil { + t.Fatalf("Reconcile returned error: %v", err) + } + + updated := &cozyv1alpha1.WorkloadMonitor{} + if err := fakeClient.Get(context.TODO(), req.NamespacedName, updated); err != nil { + t.Fatalf("Failed to get updated WorkloadMonitor: %v", err) + } + + if updated.Status.Operational == nil { + t.Fatal("Expected Operational to be set, got nil") + } + if !*updated.Status.Operational { + t.Error("Expected Operational=true (1 available >= 1 minReplicas), got false") + } +} + +func TestGetMonitorLabels(t *testing.T) { + tests := []struct { + name string + labels map[string]string + expected map[string]string + }{ + { + name: "nil labels", + labels: nil, + expected: map[string]string{}, + }, + { + name: "only workloads.cozystack.io/* labels are propagated", + labels: map[string]string{ + "workloads.cozystack.io/resource-preset": "medium", + "app.kubernetes.io/name": "postgres", + "custom.example.com/team": "platform", + }, + expected: map[string]string{ + "workloads.cozystack.io/resource-preset": "medium", + }, + }, + { + name: "monitor label is reserved and excluded", + labels: map[string]string{ + "workloads.cozystack.io/resource-preset": "small", + "workloads.cozystack.io/monitor": "should-be-dropped", + }, + expected: map[string]string{ + "workloads.cozystack.io/resource-preset": "small", + }, + }, + { + name: "multiple workloads.cozystack.io labels propagate", + labels: map[string]string{ + "workloads.cozystack.io/resource-preset": "large", + "workloads.cozystack.io/tier": "db", + }, + expected: map[string]string{ + "workloads.cozystack.io/resource-preset": "large", + "workloads.cozystack.io/tier": "db", + }, + }, + { + name: "no matching labels returns empty map", + labels: map[string]string{ + "app.kubernetes.io/name": "postgres", + }, + expected: map[string]string{}, + }, + } + + r := &WorkloadMonitorReconciler{} + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + monitor := &cozyv1alpha1.WorkloadMonitor{ + ObjectMeta: metav1.ObjectMeta{Labels: tc.labels}, + } + got := r.getMonitorLabels(monitor) + if len(got) != len(tc.expected) { + t.Fatalf("expected %d labels, got %d (%v)", len(tc.expected), len(got), got) + } + for k, v := range tc.expected { + if gv, ok := got[k]; !ok || gv != v { + t.Errorf("expected label %q=%q, got %q", k, v, gv) + } + } + }) + } +} + +func TestReconcile_MonitorLabelsPropagatedToPodWorkload(t *testing.T) { + scheme := runtime.NewScheme() + _ = cozyv1alpha1.AddToScheme(scheme) + _ = corev1.AddToScheme(scheme) + + monitor := &cozyv1alpha1.WorkloadMonitor{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-monitor", + Namespace: "default", + Labels: map[string]string{ + "workloads.cozystack.io/resource-preset": "medium", + "app.kubernetes.io/name": "ignored-not-propagated", + }, + }, + Spec: cozyv1alpha1.WorkloadMonitorSpec{ + Selector: map[string]string{"app": "test"}, + Kind: "postgres", + Type: "postgres", + }, + } + + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-pod-1", + Namespace: "default", + Labels: map[string]string{ + "app": "test", + "app.kubernetes.io/name": "pod-wins-on-conflict", + }, + }, + Status: corev1.PodStatus{ + Conditions: []corev1.PodCondition{ + {Type: corev1.PodReady, Status: corev1.ConditionTrue}, + }, + }, + } + + fakeClient := fake.NewClientBuilder(). + WithScheme(scheme). + WithObjects(monitor, pod). + WithStatusSubresource(monitor). + Build() + + reconciler := &WorkloadMonitorReconciler{Client: fakeClient, Scheme: scheme} + req := reconcile.Request{NamespacedName: types.NamespacedName{Name: "test-monitor", Namespace: "default"}} + if _, err := reconciler.Reconcile(context.TODO(), req); err != nil { + t.Fatalf("Reconcile returned error: %v", err) + } + + workload := &cozyv1alpha1.Workload{} + if err := fakeClient.Get(context.TODO(), types.NamespacedName{Name: "pod-test-pod-1", Namespace: "default"}, workload); err != nil { + t.Fatalf("Failed to get Workload: %v", err) + } + + if got := workload.Labels["workloads.cozystack.io/resource-preset"]; got != "medium" { + t.Errorf("expected monitor label propagated, got %q", got) + } + // Non-workloads.cozystack.io monitor labels must not be copied + if _, ok := workload.Labels["app.kubernetes.io/name"]; !ok { + t.Error("expected pod label to be present on Workload") + } + // Source-object label takes precedence on conflict + if got := workload.Labels["app.kubernetes.io/name"]; got != "pod-wins-on-conflict" { + t.Errorf("expected pod label to win on conflict, got %q", got) + } + // Reserved monitor label is always set from the monitor name + if got := workload.Labels["workloads.cozystack.io/monitor"]; got != "test-monitor" { + t.Errorf("expected monitor-name label, got %q", got) + } +} + +func TestReconcile_BackwardCompat_NoMonitorLabels(t *testing.T) { + scheme := runtime.NewScheme() + _ = cozyv1alpha1.AddToScheme(scheme) + _ = corev1.AddToScheme(scheme) + + monitor := &cozyv1alpha1.WorkloadMonitor{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-monitor", + Namespace: "default", + }, + Spec: cozyv1alpha1.WorkloadMonitorSpec{ + Selector: map[string]string{"app": "test"}, + }, + } + + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-pod-1", + Namespace: "default", + Labels: map[string]string{"app": "test"}, + }, + Status: corev1.PodStatus{ + Conditions: []corev1.PodCondition{ + {Type: corev1.PodReady, Status: corev1.ConditionTrue}, + }, + }, + } + + fakeClient := fake.NewClientBuilder(). + WithScheme(scheme). + WithObjects(monitor, pod). + WithStatusSubresource(monitor). + Build() + + reconciler := &WorkloadMonitorReconciler{Client: fakeClient, Scheme: scheme} + req := reconcile.Request{NamespacedName: types.NamespacedName{Name: "test-monitor", Namespace: "default"}} + if _, err := reconciler.Reconcile(context.TODO(), req); err != nil { + t.Fatalf("Reconcile returned error: %v", err) + } + + workload := &cozyv1alpha1.Workload{} + if err := fakeClient.Get(context.TODO(), types.NamespacedName{Name: "pod-test-pod-1", Namespace: "default"}, workload); err != nil { + t.Fatalf("Failed to get Workload: %v", err) + } + for k := range workload.Labels { + if strings.HasPrefix(k, "workloads.cozystack.io/") && k != "workloads.cozystack.io/monitor" { + t.Errorf("unexpected workload label present: %q", k) + } + } +} + +func TestReconcile_OperationalTrue_WhenNoMinReplicas(t *testing.T) { + scheme := runtime.NewScheme() + _ = cozyv1alpha1.AddToScheme(scheme) + _ = corev1.AddToScheme(scheme) + _ = cosiv1alpha1.AddToScheme(scheme) + + monitor := &cozyv1alpha1.WorkloadMonitor{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-monitor", + Namespace: "default", + }, + Spec: cozyv1alpha1.WorkloadMonitorSpec{ + Selector: map[string]string{"app": "test"}, + // No MinReplicas — should default to operational=true + }, + } + + fakeClient := fake.NewClientBuilder(). + WithScheme(scheme). + WithObjects(monitor). + WithStatusSubresource(monitor). + Build() + + reconciler := &WorkloadMonitorReconciler{Client: fakeClient, Scheme: scheme} + req := reconcile.Request{NamespacedName: types.NamespacedName{Name: "test-monitor", Namespace: "default"}} + + _, err := reconciler.Reconcile(context.TODO(), req) + if err != nil { + t.Fatalf("Reconcile returned error: %v", err) + } + + updated := &cozyv1alpha1.WorkloadMonitor{} + if err := fakeClient.Get(context.TODO(), req.NamespacedName, updated); err != nil { + t.Fatalf("Failed to get updated WorkloadMonitor: %v", err) + } + + if updated.Status.Operational == nil { + t.Fatal("Expected Operational to be set, got nil") + } + if !*updated.Status.Operational { + t.Error("Expected Operational=true (no MinReplicas constraint), got false") + } +} + +func newTestScheme() *runtime.Scheme { + s := runtime.NewScheme() + _ = cozyv1alpha1.AddToScheme(s) + _ = corev1.AddToScheme(s) + _ = cosiv1alpha1.AddToScheme(s) + return s +} + +func TestReconcileBucketClaimCreatesWorkload(t *testing.T) { + s := newTestScheme() + + monitor := &cozyv1alpha1.WorkloadMonitor{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-bucket", + Namespace: "tenant-demo", + }, + Spec: cozyv1alpha1.WorkloadMonitorSpec{ + Kind: "bucket", + Type: "s3", + Selector: map[string]string{ + "app.kubernetes.io/instance": "my-bucket", + }, + }, + } + + bc := &cosiv1alpha1.BucketClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-bucket", + Namespace: "tenant-demo", + Labels: map[string]string{ + "app.kubernetes.io/instance": "my-bucket", + }, + }, + Spec: cosiv1alpha1.BucketClaimSpec{ + BucketClassName: "seaweedfs", + Protocols: []cosiv1alpha1.Protocol{cosiv1alpha1.ProtocolS3}, + }, + Status: cosiv1alpha1.BucketClaimStatus{ + BucketReady: true, + BucketName: "cosi-abc123", + }, + } + + fakeClient := fake.NewClientBuilder(). + WithScheme(s). + WithObjects(monitor, bc). + WithStatusSubresource(monitor). + Build() + + reconciler := &WorkloadMonitorReconciler{Client: fakeClient, Scheme: s} + req := reconcile.Request{NamespacedName: types.NamespacedName{ + Name: "my-bucket", + Namespace: "tenant-demo", + }} + + _, err := reconciler.Reconcile(context.TODO(), req) + if err != nil { + t.Fatalf("Reconcile returned error: %v", err) + } + + workload := &cozyv1alpha1.Workload{} + err = fakeClient.Get(context.TODO(), types.NamespacedName{ + Name: "bucket-my-bucket", + Namespace: "tenant-demo", + }, workload) + if err != nil { + t.Fatalf("expected Workload to be created, got error: %v", err) + } + + if workload.Status.Kind != "bucket" { + t.Errorf("expected Kind=bucket, got %q", workload.Status.Kind) + } + if workload.Status.Type != "s3" { + t.Errorf("expected Type=s3, got %q", workload.Status.Type) + } + if !workload.Status.Operational { + t.Error("expected Operational=true for ready BucketClaim") + } +} + +func TestReconcileBucketClaimNotReady(t *testing.T) { + s := newTestScheme() + + monitor := &cozyv1alpha1.WorkloadMonitor{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-bucket", + Namespace: "tenant-demo", + }, + Spec: cozyv1alpha1.WorkloadMonitorSpec{ + Kind: "bucket", + Type: "s3", + Selector: map[string]string{ + "app.kubernetes.io/instance": "my-bucket", + }, + }, + } + + bc := &cosiv1alpha1.BucketClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-bucket", + Namespace: "tenant-demo", + Labels: map[string]string{ + "app.kubernetes.io/instance": "my-bucket", + }, + }, + Spec: cosiv1alpha1.BucketClaimSpec{ + BucketClassName: "seaweedfs", + Protocols: []cosiv1alpha1.Protocol{cosiv1alpha1.ProtocolS3}, + }, + Status: cosiv1alpha1.BucketClaimStatus{ + BucketReady: false, + }, + } + + fakeClient := fake.NewClientBuilder(). + WithScheme(s). + WithObjects(monitor, bc). + WithStatusSubresource(monitor). + Build() + + reconciler := &WorkloadMonitorReconciler{Client: fakeClient, Scheme: s} + req := reconcile.Request{NamespacedName: types.NamespacedName{ + Name: "my-bucket", + Namespace: "tenant-demo", + }} + + _, err := reconciler.Reconcile(context.TODO(), req) + if err != nil { + t.Fatalf("Reconcile returned error: %v", err) + } + + workload := &cozyv1alpha1.Workload{} + err = fakeClient.Get(context.TODO(), types.NamespacedName{ + Name: "bucket-my-bucket", + Namespace: "tenant-demo", + }, workload) + if err != nil { + t.Fatalf("expected Workload to be created, got error: %v", err) + } + + if workload.Status.Operational { + t.Error("expected Operational=false for not-ready BucketClaim") + } +} + +func TestReconcile_MonitorLabelsPropagatedToBucketClaimWorkload(t *testing.T) { + s := newTestScheme() + + monitor := &cozyv1alpha1.WorkloadMonitor{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-bucket", + Namespace: "tenant-demo", + Labels: map[string]string{ + "workloads.cozystack.io/resource-preset": "medium", + "app.kubernetes.io/name": "ignored-not-propagated", + }, + }, + Spec: cozyv1alpha1.WorkloadMonitorSpec{ + Kind: "bucket", + Type: "s3", + Selector: map[string]string{ + "app.kubernetes.io/instance": "my-bucket", + }, + }, + } + + bc := &cosiv1alpha1.BucketClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-bucket", + Namespace: "tenant-demo", + Labels: map[string]string{ + "app.kubernetes.io/instance": "my-bucket", + "app.kubernetes.io/name": "bucket-wins-on-conflict", + }, + }, + Spec: cosiv1alpha1.BucketClaimSpec{ + BucketClassName: "seaweedfs", + Protocols: []cosiv1alpha1.Protocol{cosiv1alpha1.ProtocolS3}, + }, + Status: cosiv1alpha1.BucketClaimStatus{ + BucketReady: true, + BucketName: "cosi-abc123", + }, + } + + fakeClient := fake.NewClientBuilder(). + WithScheme(s). + WithObjects(monitor, bc). + WithStatusSubresource(monitor). + Build() + + reconciler := &WorkloadMonitorReconciler{Client: fakeClient, Scheme: s} + req := reconcile.Request{NamespacedName: types.NamespacedName{ + Name: "my-bucket", + Namespace: "tenant-demo", + }} + if _, err := reconciler.Reconcile(context.TODO(), req); err != nil { + t.Fatalf("Reconcile returned error: %v", err) + } + + workload := &cozyv1alpha1.Workload{} + if err := fakeClient.Get(context.TODO(), types.NamespacedName{ + Name: "bucket-my-bucket", + Namespace: "tenant-demo", + }, workload); err != nil { + t.Fatalf("Failed to get Workload: %v", err) + } + + if got := workload.Labels["workloads.cozystack.io/resource-preset"]; got != "medium" { + t.Errorf("expected monitor label propagated, got %q", got) + } + // Source-object label takes precedence on conflict + if got := workload.Labels["app.kubernetes.io/name"]; got != "bucket-wins-on-conflict" { + t.Errorf("expected bucket claim label to win on conflict, got %q", got) + } + // Reserved monitor label is always set from the monitor name + if got := workload.Labels["workloads.cozystack.io/monitor"]; got != "my-bucket" { + t.Errorf("expected monitor-name label, got %q", got) + } +} + +func TestReconcileNoBucketClaimSkips(t *testing.T) { + s := newTestScheme() + + monitor := &cozyv1alpha1.WorkloadMonitor{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-postgres", + Namespace: "tenant-demo", + }, + Spec: cozyv1alpha1.WorkloadMonitorSpec{ + Kind: "postgres", + Type: "postgres", + Selector: map[string]string{ + "app.kubernetes.io/instance": "my-postgres", + }, + }, + } + + fakeClient := fake.NewClientBuilder(). + WithScheme(s). + WithObjects(monitor). + WithStatusSubresource(monitor). + Build() + + reconciler := &WorkloadMonitorReconciler{Client: fakeClient, Scheme: s} + req := reconcile.Request{NamespacedName: types.NamespacedName{ + Name: "my-postgres", + Namespace: "tenant-demo", + }} + + _, err := reconciler.Reconcile(context.TODO(), req) + if err != nil { + t.Fatalf("Reconcile returned error: %v", err) + } + + workloadList := &cozyv1alpha1.WorkloadList{} + err = fakeClient.List(context.TODO(), workloadList) + if err != nil { + t.Fatalf("List returned error: %v", err) + } + + for _, w := range workloadList.Items { + if w.Status.Kind == "bucket" { + t.Error("expected no bucket workloads to be created for postgres monitor") + } + } +} + +func TestQueryAllBucketMetrics(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprint(w, `{"status":"success","data":{"resultType":"vector","result":[ + {"metric":{"__name__":"SeaweedFS_s3_bucket_size_bytes","bucket":"bucket-aaa"},"value":[1713000000,"10485864"]}, + {"metric":{"__name__":"SeaweedFS_s3_bucket_physical_size_bytes","bucket":"bucket-aaa"},"value":[1713000000,"20971728"]}, + {"metric":{"__name__":"SeaweedFS_s3_bucket_size_bytes","bucket":"bucket-bbb"},"value":[1713000000,"0"]} + ]}}`) + })) + defer srv.Close() + + reconciler := &WorkloadMonitorReconciler{} + metrics := reconciler.queryAllBucketMetrics(context.TODO(), srv.URL, []string{"bucket-aaa", "bucket-bbb"}) + + bm, ok := metrics["bucket-aaa"] + if !ok { + t.Fatal("expected bucket-aaa in metrics") + } + if !bm.HasLogical || bm.LogicalSize != 10485864 { + t.Errorf("expected logical=10485864, got %d", bm.LogicalSize) + } + if !bm.HasPhysical || bm.PhysicalSize != 20971728 { + t.Errorf("expected physical=20971728, got %d", bm.PhysicalSize) + } + + bm2, ok := metrics["bucket-bbb"] + if !ok { + t.Fatal("expected bucket-bbb in metrics") + } + if !bm2.HasLogical || bm2.LogicalSize != 0 { + t.Errorf("expected logical=0 for empty bucket, got %d", bm2.LogicalSize) + } + if bm2.HasPhysical { + t.Error("expected no physical size for bucket-bbb") + } +} + +func TestQueryAllBucketMetricsEmpty(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprint(w, `{"status":"success","data":{"resultType":"vector","result":[]}}`) + })) + defer srv.Close() + + reconciler := &WorkloadMonitorReconciler{} + metrics := reconciler.queryAllBucketMetrics(context.TODO(), srv.URL, []string{"bucket-aaa", "bucket-bbb"}) + if len(metrics) != 0 { + t.Errorf("expected empty metrics, got %d", len(metrics)) + } +} + +func TestQueryAllBucketMetricsServerError(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + })) + defer srv.Close() + + reconciler := &WorkloadMonitorReconciler{} + metrics := reconciler.queryAllBucketMetrics(context.TODO(), srv.URL, []string{"bucket-aaa", "bucket-bbb"}) + if len(metrics) != 0 { + t.Errorf("expected empty metrics on error, got %d", len(metrics)) + } +} + +func TestQueryAllBucketMetricsNoURL(t *testing.T) { + reconciler := &WorkloadMonitorReconciler{} + metrics := reconciler.queryAllBucketMetrics(context.TODO(), "", nil) + if len(metrics) != 0 { + t.Errorf("expected empty metrics when URL is empty, got %d", len(metrics)) + } +} + +func TestResolvePrometheusURL(t *testing.T) { + s := newTestScheme() + + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tenant-demo", + Labels: map[string]string{ + "namespace.cozystack.io/monitoring": "tenant-root", + }, + }, + } + + fakeClient := fake.NewClientBuilder(). + WithScheme(s). + WithObjects(ns). + Build() + + reconciler := &WorkloadMonitorReconciler{Client: fakeClient, Scheme: s} + url := reconciler.resolvePrometheusURL(context.TODO(), "tenant-demo") + + expected := "http://vmselect-shortterm.tenant-root.svc:8481/select/0/prometheus" + if url != expected { + t.Errorf("expected %q, got %q", expected, url) + } +} + +func TestResolvePrometheusURLNoLabel(t *testing.T) { + s := newTestScheme() + + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tenant-demo", + }, + } + + fakeClient := fake.NewClientBuilder(). + WithScheme(s). + WithObjects(ns). + Build() + + reconciler := &WorkloadMonitorReconciler{Client: fakeClient, Scheme: s} + url := reconciler.resolvePrometheusURL(context.TODO(), "tenant-demo") + + if url != "" { + t.Errorf("expected empty URL when no monitoring label, got %q", url) + } +} + +func TestReconcileBucketClaimRequeuesWhenBucketsExist(t *testing.T) { + s := newTestScheme() + + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tenant-demo", + }, + } + + monitor := &cozyv1alpha1.WorkloadMonitor{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-bucket", + Namespace: "tenant-demo", + }, + Spec: cozyv1alpha1.WorkloadMonitorSpec{ + Kind: "bucket", + Type: "s3", + Selector: map[string]string{ + "app.kubernetes.io/instance": "my-bucket", + }, + }, + } + + bc := &cosiv1alpha1.BucketClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-bucket", + Namespace: "tenant-demo", + Labels: map[string]string{ + "app.kubernetes.io/instance": "my-bucket", + }, + }, + Spec: cosiv1alpha1.BucketClaimSpec{ + BucketClassName: "seaweedfs", + Protocols: []cosiv1alpha1.Protocol{cosiv1alpha1.ProtocolS3}, + }, + Status: cosiv1alpha1.BucketClaimStatus{ + BucketReady: true, + BucketName: "cosi-abc123", + }, + } + + fakeClient := fake.NewClientBuilder(). + WithScheme(s). + WithObjects(ns, monitor, bc). + WithStatusSubresource(monitor). + Build() + + reconciler := &WorkloadMonitorReconciler{Client: fakeClient, Scheme: s} + req := reconcile.Request{NamespacedName: types.NamespacedName{ + Name: "my-bucket", + Namespace: "tenant-demo", + }} + + result, err := reconciler.Reconcile(context.TODO(), req) + if err != nil { + t.Fatalf("Reconcile returned error: %v", err) + } + + if result.RequeueAfter == 0 { + t.Error("expected RequeueAfter > 0 when buckets exist") + } + + workload := &cozyv1alpha1.Workload{} + err = fakeClient.Get(context.TODO(), types.NamespacedName{ + Name: "bucket-my-bucket", + Namespace: "tenant-demo", + }, workload) + if err != nil { + t.Fatalf("expected Workload to be created, got error: %v", err) + } + + // Without monitoring label on namespace, no size metrics should be set + if _, ok := workload.Status.Resources["s3-storage-bytes"]; ok { + t.Error("expected no s3-storage-bytes when monitoring is not configured") + } + if len(workload.Status.Resources) != 0 { + t.Errorf("expected empty resources without monitoring, got %v", workload.Status.Resources) + } +} diff --git a/internal/crdinstall/manifests/cozystack.io_packagesources.yaml b/internal/crdinstall/manifests/cozystack.io_packagesources.yaml index 0acfcdd2..1d0037df 100644 --- a/internal/crdinstall/manifests/cozystack.io_packagesources.yaml +++ b/internal/crdinstall/manifests/cozystack.io_packagesources.yaml @@ -118,6 +118,19 @@ spec: ReleaseName is the name of the HelmRelease resource that will be created If not specified, defaults to the component Name field type: string + upgradeCRDs: + description: |- + UpgradeCRDs controls how CRDs from the chart's crds/ directory are + handled on HelmRelease upgrades. Maps to HelmRelease.Spec.Upgrade.CRDs. + Empty string (default) preserves the helm-controller default (Skip). + Use "CreateReplace" for operators that evolve their CRD set between + versions. Warning: CreateReplace overwrites CRDs and may cause data + loss if upstream drops fields from a CRD with live objects. + enum: + - Skip + - Create + - CreateReplace + type: string type: object libraries: description: |- diff --git a/internal/lineagecontrollerwebhook/webhook.go b/internal/lineagecontrollerwebhook/webhook.go index 299cbaee..cf871fb0 100644 --- a/internal/lineagecontrollerwebhook/webhook.go +++ b/internal/lineagecontrollerwebhook/webhook.go @@ -8,22 +8,28 @@ import ( "strings" "github.com/cozystack/cozystack/pkg/lineage" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/apiutil" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1" + appsv1alpha1 "github.com/cozystack/cozystack/pkg/apis/apps/v1alpha1" corev1alpha1 "github.com/cozystack/cozystack/pkg/apis/core/v1alpha1" + schedulerapi "github.com/cozystack/cozystack-scheduler/pkg/apis/v1alpha1" ) var ( - NoAncestors = fmt.Errorf("no managed apps found in lineage") - AncestryAmbiguous = fmt.Errorf("object ancestry is ambiguous") + NoAncestors = errors.New("no managed apps found in lineage") + AncestryAmbiguous = errors.New("object ancestry is ambiguous") ) const ( @@ -95,26 +101,29 @@ func (h *LineageControllerWebhook) Handle(ctx context.Context, req admission.Req return admission.Errored(400, fmt.Errorf("decode object: %w", err)) } - labels, err := h.computeLabels(ctx, obj) - for { - if err != nil && errors.Is(err, NoAncestors) { - break // not a problem, mark object as unmanaged - } - if err != nil && errors.Is(err, AncestryAmbiguous) { - warn = append(warn, "object ancestry ambiguous, using first ancestor found") - break - } - if err != nil { - logger.Error(err, "error computing lineage labels") - return admission.Errored(500, fmt.Errorf("error computing lineage labels: %w", err)) - } - if err == nil { - break - } + owner, err := h.getOwner(ctx, obj) + switch { + case err != nil && errors.Is(err, AncestryAmbiguous): + warn = append(warn, "object ancestry ambiguous, using first ancestor found") + case err != nil && errors.Is(err, NoAncestors): + // not a problem, mark object as unmanaged + case err != nil: + logger.Error(err, "error computing lineage labels") + return admission.Errored(500, fmt.Errorf("error computing lineage labels: %w", err)) + } + labels, err := h.computeLabels(ctx, obj, owner) + if err != nil { + logger.Error(err, "error computing lineage labels") + return admission.Errored(500, fmt.Errorf("error computing lineage labels: %w", err)) } h.applyLabels(obj, labels) + if err := h.applySchedulingClass(ctx, obj, owner, req.Namespace); err != nil { + logger.Error(err, "error applying scheduling class") + return admission.Errored(500, fmt.Errorf("error applying scheduling class: %w", err)) + } + mutated, err := json.Marshal(obj) if err != nil { return admission.Errored(500, fmt.Errorf("marshal mutated pod: %w", err)) @@ -123,23 +132,30 @@ func (h *LineageControllerWebhook) Handle(ctx context.Context, req admission.Req return admission.PatchResponseFromRaw(req.Object.Raw, mutated).WithWarnings(warn...) } -func (h *LineageControllerWebhook) computeLabels(ctx context.Context, o *unstructured.Unstructured) (map[string]string, error) { +func (h *LineageControllerWebhook) getOwner(ctx context.Context, o *unstructured.Unstructured) (*unstructured.Unstructured, error) { owners := lineage.WalkOwnershipGraph(ctx, h.dynClient, h.mapper, h, o) if len(owners) == 0 { - return map[string]string{ManagedObjectKey: "false"}, NoAncestors + return nil, NoAncestors } obj, err := owners[0].GetUnstructured(ctx, h.dynClient, h.mapper) if err != nil { return nil, err } - gv, err := schema.ParseGroupVersion(obj.GetAPIVersion()) - if err != nil { - // should never happen, we got an APIVersion right from the API - return nil, fmt.Errorf("could not parse APIVersion %s to a group and version: %w", obj.GetAPIVersion(), err) - } if len(owners) > 1 { err = AncestryAmbiguous } + return obj, err +} + +func (h *LineageControllerWebhook) computeLabels(ctx context.Context, obj *unstructured.Unstructured, owner *unstructured.Unstructured) (map[string]string, error) { + if owner == nil { + return nil, nil + } + gv, err := schema.ParseGroupVersion(owner.GetAPIVersion()) + if err != nil { + // should never happen, we got an APIVersion right from the API + return nil, fmt.Errorf("could not parse APIVersion %s to a group and version: %w", owner.GetAPIVersion(), err) + } labels := map[string]string{ // truncate apigroup to first 63 chars ManagedObjectKey: "true", @@ -153,25 +169,25 @@ func (h *LineageControllerWebhook) computeLabels(ctx context.Context, o *unstruc } return s }(gv.Group), - ManagerKindKey: obj.GetKind(), - ManagerNameKey: obj.GetName(), + ManagerKindKey: owner.GetKind(), + ManagerNameKey: owner.GetName(), } templateLabels := map[string]string{ - "kind": strings.ToLower(obj.GetKind()), - "name": obj.GetName(), - "namespace": o.GetNamespace(), + "kind": strings.ToLower(owner.GetKind()), + "name": owner.GetName(), + "namespace": obj.GetNamespace(), } cfg := h.config.Load().(*runtimeConfig) - crd := cfg.appCRDMap[appRef{gv.Group, obj.GetKind()}] - resourceSelectors := h.getResourceSelectors(o.GroupVersionKind().GroupKind(), crd) + crd := cfg.appCRDMap[appRef{gv.Group, owner.GetKind()}] + resourceSelectors := h.getResourceSelectors(obj.GroupVersionKind().GroupKind(), crd) labels[corev1alpha1.TenantResourceLabelKey] = func(b bool) string { if b { return corev1alpha1.TenantResourceLabelValue } return "false" - }(matchResourceToExcludeInclude(ctx, o.GetName(), templateLabels, o.GetLabels(), resourceSelectors)) - return labels, err + }(matchResourceToExcludeInclude(ctx, obj.GetName(), templateLabels, obj.GetLabels(), resourceSelectors)) + return labels, nil } func (h *LineageControllerWebhook) applyLabels(o *unstructured.Unstructured, labels map[string]string) { @@ -185,6 +201,68 @@ func (h *LineageControllerWebhook) applyLabels(o *unstructured.Unstructured, lab o.SetLabels(existing) } +// applySchedulingClass injects schedulerName and scheduling-class annotation +// into Pods whose namespace carries the scheduler.cozystack.io/scheduling-class label. +// If the referenced SchedulingClass CR does not exist (e.g. the scheduler +// package is not installed), the injection is silently skipped so that pods +// are not left Pending. +func (h *LineageControllerWebhook) applySchedulingClass(ctx context.Context, obj, owner *unstructured.Unstructured, namespace string) error { + if obj.GetKind() != "Pod" { + return nil + } + + // Determine scheduling class: owner Application field takes priority, + // then fall back to namespace label. + var schedulingClass string + if owner != nil { + var app appsv1alpha1.Application + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(owner.Object, &app); err == nil { + schedulingClass = app.SchedulingClass() + } + } + if schedulingClass == "" { + ns := &corev1.Namespace{} + if err := h.Get(ctx, client.ObjectKey{Name: namespace}, ns); err != nil { + return fmt.Errorf("getting namespace %s: %w", namespace, err) + } + schedulingClass = ns.Labels[schedulerapi.SchedulingClassLabel] + } + + if schedulingClass == "" { + return nil + } + + // Verify that the referenced SchedulingClass CR exists. + // If the CRD is not installed or the CR is missing, skip injection + // so that pods are not stuck Pending on a non-existent scheduler. + _, err := h.dynClient.Resource(schedulingClassGVR).Get(ctx, schedulingClass, metav1.GetOptions{}) + if err != nil { + logger := log.FromContext(ctx) + logger.Info("SchedulingClass not found, skipping scheduler injection", + "schedulingClass", schedulingClass, "namespace", namespace) + return nil + } + + if err := unstructured.SetNestedField(obj.Object, schedulerapi.SchedulerName, "spec", "schedulerName"); err != nil { + return fmt.Errorf("setting schedulerName: %w", err) + } + + annotations := obj.GetAnnotations() + if annotations == nil { + annotations = make(map[string]string) + } + annotations[schedulerapi.SchedulingClassAnnotation] = schedulingClass + obj.SetAnnotations(annotations) + + return nil +} + +var schedulingClassGVR = schema.GroupVersionResource{ + Group: schedulerapi.Group, + Version: schedulerapi.Version, + Resource: schedulerapi.Resource, +} + func (h *LineageControllerWebhook) decodeUnstructured(req admission.Request, out *unstructured.Unstructured) error { if h.decoder != nil { if err := h.decoder.Decode(req, out); err == nil { diff --git a/internal/operator/package_reconciler.go b/internal/operator/package_reconciler.go index 32e667e4..0e724e49 100644 --- a/internal/operator/package_reconciler.go +++ b/internal/operator/package_reconciler.go @@ -45,6 +45,16 @@ const ( SecretCozystackValues = "cozystack-values" ) +// parseCRDPolicy maps ComponentInstall.UpgradeCRDs to a helmv2.CRDsPolicy. +// Empty / nil preserves the helm-controller default (Skip on upgrade); +// the CRD enum marker restricts the string to Skip/Create/CreateReplace. +func parseCRDPolicy(install *cozyv1alpha1.ComponentInstall) helmv2.CRDsPolicy { + if install == nil || install.UpgradeCRDs == "" { + return "" + } + return helmv2.CRDsPolicy(install.UpgradeCRDs) +} + // PackageReconciler reconciles Package resources type PackageReconciler struct { client.Client @@ -221,6 +231,7 @@ func (r *PackageReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct Remediation: &helmv2.UpgradeRemediation{ Retries: -1, }, + CRDs: parseCRDPolicy(component.Install), }, }, } diff --git a/internal/operator/package_reconciler_test.go b/internal/operator/package_reconciler_test.go new file mode 100644 index 00000000..f0ee2c19 --- /dev/null +++ b/internal/operator/package_reconciler_test.go @@ -0,0 +1,138 @@ +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package operator + +import ( + "encoding/json" + "os" + "path/filepath" + "testing" + + cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1" + helmv2 "github.com/fluxcd/helm-controller/api/v2" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "sigs.k8s.io/yaml" +) + +func TestParseCRDPolicy(t *testing.T) { + tests := []struct { + name string + install *cozyv1alpha1.ComponentInstall + want helmv2.CRDsPolicy + }{ + { + name: "nil install leaves flux default", + install: nil, + want: "", + }, + { + name: "empty upgradeCRDs leaves flux default", + install: &cozyv1alpha1.ComponentInstall{}, + want: "", + }, + { + name: "Skip is passed through", + install: &cozyv1alpha1.ComponentInstall{UpgradeCRDs: "Skip"}, + want: helmv2.Skip, + }, + { + name: "Create is passed through", + install: &cozyv1alpha1.ComponentInstall{UpgradeCRDs: "Create"}, + want: helmv2.Create, + }, + { + name: "CreateReplace is passed through", + install: &cozyv1alpha1.ComponentInstall{UpgradeCRDs: "CreateReplace"}, + want: helmv2.CreateReplace, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := parseCRDPolicy(tc.install) + if got != tc.want { + t.Errorf("parseCRDPolicy() = %q, want %q", got, tc.want) + } + }) + } +} + +// TestPackageSourceCRDHasUpgradeCRDsEnum guards the generated CRD schema: the +// invalid-value case from the spec is enforced at the API server via a +// kubebuilder enum marker, not in the reconciler. If someone drops the marker +// and forgets to regenerate, this test catches it. +func TestPackageSourceCRDHasUpgradeCRDsEnum(t *testing.T) { + path := filepath.Join("..", "crdinstall", "manifests", "cozystack.io_packagesources.yaml") + data, err := os.ReadFile(path) + if err != nil { + t.Fatalf("read %s: %v", path, err) + } + + var crd apiextensionsv1.CustomResourceDefinition + if err := yaml.Unmarshal(data, &crd); err != nil { + t.Fatalf("unmarshal CRD: %v", err) + } + + var field *apiextensionsv1.JSONSchemaProps + for i := range crd.Spec.Versions { + v := &crd.Spec.Versions[i] + if v.Schema == nil || v.Schema.OpenAPIV3Schema == nil { + continue + } + spec, ok := v.Schema.OpenAPIV3Schema.Properties["spec"] + if !ok { + continue + } + variants, ok := spec.Properties["variants"] + if !ok || variants.Items == nil || variants.Items.Schema == nil { + continue + } + components, ok := variants.Items.Schema.Properties["components"] + if !ok || components.Items == nil || components.Items.Schema == nil { + continue + } + install, ok := components.Items.Schema.Properties["install"] + if !ok { + continue + } + f, ok := install.Properties["upgradeCRDs"] + if !ok { + continue + } + field = &f + break + } + + if field == nil { + t.Fatal("upgradeCRDs field not found in PackageSource CRD schema") + } + + got := map[string]bool{} + for _, e := range field.Enum { + var s string + if err := json.Unmarshal(e.Raw, &s); err != nil { + t.Fatalf("unmarshal enum value %q: %v", e.Raw, err) + } + got[s] = true + } + + for _, want := range []string{"Skip", "Create", "CreateReplace"} { + if !got[want] { + t.Errorf("enum value %q missing from upgradeCRDs; got %v", want, got) + } + } +} diff --git a/packages/apps/bucket/Makefile b/packages/apps/bucket/Makefile index 27cd199b..1e35bd53 100644 --- a/packages/apps/bucket/Makefile +++ b/packages/apps/bucket/Makefile @@ -1,6 +1,5 @@ include ../../../hack/package.mk generate: - cozyvalues-gen -v values.yaml -s values.schema.json -r README.md - yq -o json -i '.properties = {}' values.schema.json + cozyvalues-gen -m 'bucket' -v values.yaml -s values.schema.json -r README.md -g ../../../api/apps/v1alpha1/bucket/types.go ../../../hack/update-crd.sh diff --git a/packages/apps/bucket/README.md b/packages/apps/bucket/README.md index 89749b1d..982225ce 100644 --- a/packages/apps/bucket/README.md +++ b/packages/apps/bucket/README.md @@ -1,3 +1,13 @@ # S3 bucket ## Parameters + +### Parameters + +| Name | Description | Type | Value | +| ---------------------- | -------------------------------------------------------------------------- | ------------------- | ------- | +| `locking` | Provisions bucket from the `-lock` BucketClass (with object lock enabled). | `bool` | `false` | +| `storagePool` | Selects a specific BucketClass by storage pool name. | `string` | `""` | +| `users` | Users configuration map. | `map[string]object` | `{}` | +| `users[name].readonly` | Whether the user has read-only access. | `bool` | `false` | + diff --git a/packages/apps/bucket/templates/bucketclaim.yaml b/packages/apps/bucket/templates/bucketclaim.yaml index 5cfdc1c1..0639916f 100644 --- a/packages/apps/bucket/templates/bucketclaim.yaml +++ b/packages/apps/bucket/templates/bucketclaim.yaml @@ -1,19 +1,24 @@ {{- $seaweedfs := .Values._namespace.seaweedfs }} +{{- $pool := .Values.storagePool }} apiVersion: objectstorage.k8s.io/v1alpha1 kind: BucketClaim metadata: name: {{ .Release.Name }} + labels: + app.kubernetes.io/instance: {{ .Release.Name }} spec: - bucketClassName: {{ $seaweedfs }} + bucketClassName: {{ $seaweedfs }}{{- if $pool }}-{{ $pool }}{{- end }}{{- if .Values.locking }}-lock{{- end }} protocols: - s3 +{{- range $name, $user := .Values.users }} --- apiVersion: objectstorage.k8s.io/v1alpha1 kind: BucketAccess metadata: - name: {{ .Release.Name }} + name: {{ $.Release.Name }}-{{ $name }} spec: - bucketAccessClassName: {{ $seaweedfs }} - bucketClaimName: {{ .Release.Name }} - credentialsSecretName: {{ .Release.Name }} + bucketAccessClassName: {{ $seaweedfs }}{{- if $pool }}-{{ $pool }}{{- end }}{{- if $user.readonly }}-readonly{{- end }} + bucketClaimName: {{ $.Release.Name }} + credentialsSecretName: {{ $.Release.Name }}-{{ $name }} protocol: s3 +{{- end }} diff --git a/packages/apps/bucket/templates/dashboard-resourcemap.yaml b/packages/apps/bucket/templates/dashboard-resourcemap.yaml index 5edc8b7a..82a51e16 100644 --- a/packages/apps/bucket/templates/dashboard-resourcemap.yaml +++ b/packages/apps/bucket/templates/dashboard-resourcemap.yaml @@ -8,8 +8,9 @@ rules: resources: - secrets resourceNames: - - {{ .Release.Name }} - - {{ .Release.Name }}-credentials + {{- range $name, $user := .Values.users }} + - {{ $.Release.Name }}-{{ $name }}-credentials + {{- end }} verbs: ["get", "list", "watch"] - apiGroups: - networking.k8s.io diff --git a/packages/apps/bucket/templates/helmrelease.yaml b/packages/apps/bucket/templates/helmrelease.yaml index 704470a8..d3290512 100644 --- a/packages/apps/bucket/templates/helmrelease.yaml +++ b/packages/apps/bucket/templates/helmrelease.yaml @@ -23,3 +23,4 @@ spec: name: cozystack-values values: bucketName: {{ .Release.Name }} + users: {{ .Values.users | toJson }} diff --git a/packages/apps/bucket/templates/workloadmonitor.yaml b/packages/apps/bucket/templates/workloadmonitor.yaml new file mode 100644 index 00000000..a23f3147 --- /dev/null +++ b/packages/apps/bucket/templates/workloadmonitor.yaml @@ -0,0 +1,13 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: WorkloadMonitor +metadata: + name: {{ $.Release.Name }} +spec: + replicas: 0 + minReplicas: 0 + kind: bucket + type: s3 + selector: + app.kubernetes.io/instance: {{ $.Release.Name }} + version: {{ $.Chart.Version }} diff --git a/packages/apps/bucket/values.schema.json b/packages/apps/bucket/values.schema.json index 167bc53e..d302842c 100644 --- a/packages/apps/bucket/values.schema.json +++ b/packages/apps/bucket/values.schema.json @@ -1,5 +1,30 @@ { "title": "Chart Values", "type": "object", - "properties": {} + "properties": { + "locking": { + "description": "Provisions bucket from the `-lock` BucketClass (with object lock enabled).", + "type": "boolean", + "default": false + }, + "storagePool": { + "description": "Selects a specific BucketClass by storage pool name.", + "type": "string", + "default": "" + }, + "users": { + "description": "Users configuration map.", + "type": "object", + "default": {}, + "additionalProperties": { + "type": "object", + "properties": { + "readonly": { + "description": "Whether the user has read-only access.", + "type": "boolean" + } + } + } + } + } } diff --git a/packages/apps/bucket/values.yaml b/packages/apps/bucket/values.yaml index 0967ef42..df284df7 100644 --- a/packages/apps/bucket/values.yaml +++ b/packages/apps/bucket/values.yaml @@ -1 +1,11 @@ -{} +## @param {bool} locking=false - Provisions bucket from the `-lock` BucketClass (with object lock enabled). +locking: false + +## @param {string} [storagePool] - Selects a specific BucketClass by storage pool name. +storagePool: "" + +## @typedef {struct} User - Bucket user configuration. +## @field {bool} [readonly] - Whether the user has read-only access. + +## @param {map[string]User} users - Users configuration map. +users: {} diff --git a/packages/apps/clickhouse/Makefile b/packages/apps/clickhouse/Makefile index 2c77add9..2d671d1d 100644 --- a/packages/apps/clickhouse/Makefile +++ b/packages/apps/clickhouse/Makefile @@ -4,7 +4,7 @@ include ../../../hack/common-envs.mk include ../../../hack/package.mk generate: - cozyvalues-gen -v values.yaml -s values.schema.json -r README.md + cozyvalues-gen -m 'clickhouse' -v values.yaml -s values.schema.json -r README.md -g ../../../api/apps/v1alpha1/clickhouse/types.go ../../../hack/update-crd.sh image: diff --git a/packages/apps/clickhouse/templates/workloadmonitor.yaml b/packages/apps/clickhouse/templates/workloadmonitor.yaml index 9020ad41..62c951c2 100644 --- a/packages/apps/clickhouse/templates/workloadmonitor.yaml +++ b/packages/apps/clickhouse/templates/workloadmonitor.yaml @@ -3,6 +3,8 @@ apiVersion: cozystack.io/v1alpha1 kind: WorkloadMonitor metadata: name: {{ $.Release.Name }} + labels: + workloads.cozystack.io/resource-preset: {{ .Values.resourcesPreset | quote }} spec: replicas: {{ .Values.replicas }} minReplicas: 1 @@ -17,6 +19,8 @@ apiVersion: cozystack.io/v1alpha1 kind: WorkloadMonitor metadata: name: {{ $.Release.Name }}-keeper + labels: + workloads.cozystack.io/resource-preset: {{ .Values.clickhouseKeeper.resourcesPreset | quote }} spec: replicas: {{ .Values.clickhouseKeeper.replicas }} minReplicas: 1 diff --git a/packages/apps/clickhouse/values.schema.json b/packages/apps/clickhouse/values.schema.json index abb3aeb1..e4e1df7d 100644 --- a/packages/apps/clickhouse/values.schema.json +++ b/packages/apps/clickhouse/values.schema.json @@ -2,6 +2,119 @@ "title": "Chart Values", "type": "object", "properties": { + "replicas": { + "description": "Number of ClickHouse replicas.", + "type": "integer", + "default": 2 + }, + "shards": { + "description": "Number of ClickHouse shards.", + "type": "integer", + "default": 1 + }, + "resources": { + "description": "Explicit CPU and memory configuration for each ClickHouse replica. When omitted, the preset defined in `resourcesPreset` is applied.", + "type": "object", + "default": {}, + "properties": { + "cpu": { + "description": "CPU available to each replica.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "memory": { + "description": "Memory (RAM) available to each replica.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + } + } + }, + "resourcesPreset": { + "description": "Default sizing preset used when `resources` is omitted.", + "type": "string", + "default": "small", + "enum": [ + "nano", + "micro", + "small", + "medium", + "large", + "xlarge", + "2xlarge" + ] + }, + "size": { + "description": "Persistent Volume Claim size available for application data.", + "default": "10Gi", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "storageClass": { + "description": "StorageClass used to store the data.", + "type": "string", + "default": "" + }, + "logStorageSize": { + "description": "Size of Persistent Volume for logs.", + "default": "2Gi", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "logTTL": { + "description": "TTL (expiration time) for `query_log` and `query_thread_log`.", + "type": "integer", + "default": 15 + }, + "users": { + "description": "Users configuration map.", + "type": "object", + "default": {}, + "additionalProperties": { + "type": "object", + "properties": { + "password": { + "description": "Password for the user.", + "type": "string" + }, + "readonly": { + "description": "User is readonly (default: false).", + "type": "boolean" + } + } + } + }, "backup": { "description": "Backup configuration.", "type": "object", @@ -103,119 +216,6 @@ "x-kubernetes-int-or-string": true } } - }, - "logStorageSize": { - "description": "Size of Persistent Volume for logs.", - "default": "2Gi", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - }, - "logTTL": { - "description": "TTL (expiration time) for `query_log` and `query_thread_log`.", - "type": "integer", - "default": 15 - }, - "replicas": { - "description": "Number of ClickHouse replicas.", - "type": "integer", - "default": 2 - }, - "resources": { - "description": "Explicit CPU and memory configuration for each ClickHouse replica. When omitted, the preset defined in `resourcesPreset` is applied.", - "type": "object", - "default": {}, - "properties": { - "cpu": { - "description": "CPU available to each replica.", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - }, - "memory": { - "description": "Memory (RAM) available to each replica.", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - } - } - }, - "resourcesPreset": { - "description": "Default sizing preset used when `resources` is omitted.", - "type": "string", - "default": "small", - "enum": [ - "nano", - "micro", - "small", - "medium", - "large", - "xlarge", - "2xlarge" - ] - }, - "shards": { - "description": "Number of ClickHouse shards.", - "type": "integer", - "default": 1 - }, - "size": { - "description": "Persistent Volume Claim size available for application data.", - "default": "10Gi", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - }, - "storageClass": { - "description": "StorageClass used to store the data.", - "type": "string", - "default": "" - }, - "users": { - "description": "Users configuration map.", - "type": "object", - "default": {}, - "additionalProperties": { - "type": "object", - "properties": { - "password": { - "description": "Password for the user.", - "type": "string" - }, - "readonly": { - "description": "User is readonly (default: false).", - "type": "boolean" - } - } - } } } -} \ No newline at end of file +} diff --git a/packages/apps/foundationdb/Makefile b/packages/apps/foundationdb/Makefile index 76c84980..1a07cd5b 100644 --- a/packages/apps/foundationdb/Makefile +++ b/packages/apps/foundationdb/Makefile @@ -1,4 +1,4 @@ include ../../../hack/package.mk generate: - cozyvalues-gen -v values.yaml -s values.schema.json -r README.md \ No newline at end of file + cozyvalues-gen -m 'foundationdb' -v values.yaml -s values.schema.json -r README.md -g ../../../api/apps/v1alpha1/foundationdb/types.go \ No newline at end of file diff --git a/packages/apps/foundationdb/README.md b/packages/apps/foundationdb/README.md index 4f7b6878..30f5714e 100644 --- a/packages/apps/foundationdb/README.md +++ b/packages/apps/foundationdb/README.md @@ -1,4 +1,4 @@ -# FoundationDB +# Managed FoundationDB Service A managed FoundationDB service for Cozystack. diff --git a/packages/apps/foundationdb/templates/workloadmonitor.yaml b/packages/apps/foundationdb/templates/workloadmonitor.yaml index 306797f3..2e2a3d19 100644 --- a/packages/apps/foundationdb/templates/workloadmonitor.yaml +++ b/packages/apps/foundationdb/templates/workloadmonitor.yaml @@ -8,6 +8,7 @@ metadata: app.kubernetes.io/name: foundationdb app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/managed-by: {{ .Release.Service }} + workloads.cozystack.io/resource-preset: {{ .Values.resourcesPreset | quote }} spec: replicas: {{ .Values.cluster.processCounts.storage }} minReplicas: {{ include "foundationdb.minReplicas" . }} diff --git a/packages/apps/foundationdb/values.schema.json b/packages/apps/foundationdb/values.schema.json index 7b3a1ba5..1fc77d59 100644 --- a/packages/apps/foundationdb/values.schema.json +++ b/packages/apps/foundationdb/values.schema.json @@ -2,82 +2,6 @@ "title": "Chart Values", "type": "object", "properties": { - "automaticReplacements": { - "description": "Enable automatic pod replacements.", - "type": "boolean", - "default": true - }, - "backup": { - "description": "Backup configuration.", - "type": "object", - "default": {}, - "required": [ - "enabled", - "retentionPolicy", - "s3" - ], - "properties": { - "enabled": { - "description": "Enable backups.", - "type": "boolean", - "default": false - }, - "retentionPolicy": { - "description": "Retention policy for backups.", - "type": "string", - "default": "7d" - }, - "s3": { - "description": "S3 configuration for backups.", - "type": "object", - "default": {}, - "required": [ - "bucket", - "credentials", - "endpoint", - "region" - ], - "properties": { - "bucket": { - "description": "S3 bucket name.", - "type": "string", - "default": "" - }, - "credentials": { - "description": "S3 credentials.", - "type": "object", - "default": {}, - "required": [ - "accessKeyId", - "secretAccessKey" - ], - "properties": { - "accessKeyId": { - "description": "S3 access key ID.", - "type": "string", - "default": "" - }, - "secretAccessKey": { - "description": "S3 secret access key.", - "type": "string", - "default": "" - } - } - }, - "endpoint": { - "description": "S3 endpoint URL.", - "type": "string", - "default": "" - }, - "region": { - "description": "S3 region.", - "type": "string", - "default": "us-east-1" - } - } - } - } - }, "cluster": { "description": "Cluster configuration.", "type": "object", @@ -155,35 +79,33 @@ } } }, - "customParameters": { - "description": "Custom parameters to pass to FoundationDB.", - "type": "array", - "default": [], - "items": { - "type": "string" - } - }, - "imageType": { - "description": "Container image deployment type.", - "type": "string", - "default": "unified", - "enum": [ - "unified", - "split" - ] - }, - "monitoring": { - "description": "Monitoring configuration.", + "storage": { + "description": "Storage configuration.", "type": "object", "default": {}, "required": [ - "enabled" + "size", + "storageClass" ], "properties": { - "enabled": { - "description": "Enable WorkloadMonitor integration.", - "type": "boolean", - "default": true + "size": { + "description": "Size of persistent volumes for each instance.", + "default": "16Gi", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "storageClass": { + "description": "Storage class (if not set, uses cluster default).", + "type": "string", + "default": "" } } }, @@ -232,6 +154,109 @@ "2xlarge" ] }, + "backup": { + "description": "Backup configuration.", + "type": "object", + "default": {}, + "required": [ + "enabled", + "retentionPolicy", + "s3" + ], + "properties": { + "enabled": { + "description": "Enable backups.", + "type": "boolean", + "default": false + }, + "retentionPolicy": { + "description": "Retention policy for backups.", + "type": "string", + "default": "7d" + }, + "s3": { + "description": "S3 configuration for backups.", + "type": "object", + "default": {}, + "required": [ + "bucket", + "credentials", + "endpoint", + "region" + ], + "properties": { + "bucket": { + "description": "S3 bucket name.", + "type": "string", + "default": "" + }, + "credentials": { + "description": "S3 credentials.", + "type": "object", + "default": {}, + "required": [ + "accessKeyId", + "secretAccessKey" + ], + "properties": { + "accessKeyId": { + "description": "S3 access key ID.", + "type": "string", + "default": "" + }, + "secretAccessKey": { + "description": "S3 secret access key.", + "type": "string", + "default": "" + } + } + }, + "endpoint": { + "description": "S3 endpoint URL.", + "type": "string", + "default": "" + }, + "region": { + "description": "S3 region.", + "type": "string", + "default": "us-east-1" + } + } + } + } + }, + "monitoring": { + "description": "Monitoring configuration.", + "type": "object", + "default": {}, + "required": [ + "enabled" + ], + "properties": { + "enabled": { + "description": "Enable WorkloadMonitor integration.", + "type": "boolean", + "default": true + } + } + }, + "customParameters": { + "description": "Custom parameters to pass to FoundationDB.", + "type": "array", + "default": [], + "items": { + "type": "string" + } + }, + "imageType": { + "description": "Container image deployment type.", + "type": "string", + "default": "unified", + "enum": [ + "unified", + "split" + ] + }, "securityContext": { "description": "Security context for containers.", "type": "object", @@ -253,35 +278,10 @@ } } }, - "storage": { - "description": "Storage configuration.", - "type": "object", - "default": {}, - "required": [ - "size", - "storageClass" - ], - "properties": { - "size": { - "description": "Size of persistent volumes for each instance.", - "default": "16Gi", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - }, - "storageClass": { - "description": "Storage class (if not set, uses cluster default).", - "type": "string", - "default": "" - } - } + "automaticReplacements": { + "description": "Enable automatic pod replacements.", + "type": "boolean", + "default": true } } -} \ No newline at end of file +} diff --git a/packages/apps/harbor/Makefile b/packages/apps/harbor/Makefile index f0ce9944..43c25998 100644 --- a/packages/apps/harbor/Makefile +++ b/packages/apps/harbor/Makefile @@ -3,5 +3,5 @@ NAME=harbor include ../../../hack/package.mk generate: - cozyvalues-gen -v values.yaml -s values.schema.json -r README.md + cozyvalues-gen -m 'harbor' -v values.yaml -s values.schema.json -r README.md -g ../../../api/apps/v1alpha1/harbor/types.go ../../../hack/update-crd.sh diff --git a/packages/apps/harbor/README.md b/packages/apps/harbor/README.md index df44ca8d..7789d9fe 100644 --- a/packages/apps/harbor/README.md +++ b/packages/apps/harbor/README.md @@ -1,6 +1,6 @@ # Managed Harbor Container Registry -Harbor is an open source trusted cloud native registry project that stores, signs, and scans content. +Harbor is an open-source trusted cloud-native registry project that stores, signs, and scans content. ## Parameters diff --git a/packages/apps/harbor/templates/harbor.yaml b/packages/apps/harbor/templates/harbor.yaml index ff8e88ad..bf780967 100644 --- a/packages/apps/harbor/templates/harbor.yaml +++ b/packages/apps/harbor/templates/harbor.yaml @@ -158,6 +158,8 @@ apiVersion: cozystack.io/v1alpha1 kind: WorkloadMonitor metadata: name: {{ $.Release.Name }}-core + labels: + workloads.cozystack.io/resource-preset: {{ .Values.core.resourcesPreset | quote }} spec: replicas: 1 minReplicas: 1 @@ -174,6 +176,8 @@ apiVersion: cozystack.io/v1alpha1 kind: WorkloadMonitor metadata: name: {{ $.Release.Name }}-registry + labels: + workloads.cozystack.io/resource-preset: {{ .Values.registry.resourcesPreset | quote }} spec: replicas: 1 minReplicas: 1 diff --git a/packages/apps/harbor/templates/ingress.yaml b/packages/apps/harbor/templates/ingress.yaml index 28691654..70933f5f 100644 --- a/packages/apps/harbor/templates/ingress.yaml +++ b/packages/apps/harbor/templates/ingress.yaml @@ -1,7 +1,8 @@ {{- $ingress := .Values._namespace.ingress }} {{- $host := .Values._namespace.host }} {{- $harborHost := .Values.host | default (printf "%s.%s" .Release.Name $host) }} -{{- $issuerType := (index .Values._cluster "clusterissuer") | default "http01" }} +{{- $solver := (index .Values._cluster "solver") | default "http01" }} +{{- $clusterIssuer := (index .Values._cluster "issuer-name") | default "letsencrypt-prod" }} --- apiVersion: networking.k8s.io/v1 kind: Ingress @@ -13,10 +14,10 @@ metadata: nginx.ingress.kubernetes.io/proxy-send-timeout: "900" nginx.ingress.kubernetes.io/ssl-redirect: "true" nginx.ingress.kubernetes.io/backend-protocol: "HTTP" - {{- if ne $issuerType "cloudflare" }} - acme.cert-manager.io/http01-ingress-class: {{ $ingress }} + {{- if eq $solver "http01" }} + acme.cert-manager.io/http01-ingress-ingressclassname: {{ $ingress }} {{- end }} - cert-manager.io/cluster-issuer: letsencrypt-prod + cert-manager.io/cluster-issuer: {{ $clusterIssuer }} spec: ingressClassName: {{ $ingress }} tls: diff --git a/packages/apps/harbor/values.schema.json b/packages/apps/harbor/values.schema.json index 1352f5dc..23860ca3 100644 --- a/packages/apps/harbor/values.schema.json +++ b/packages/apps/harbor/values.schema.json @@ -2,6 +2,16 @@ "title": "Chart Values", "type": "object", "properties": { + "host": { + "description": "Hostname for external access to Harbor (defaults to 'harbor' subdomain for the tenant host).", + "type": "string", + "default": "" + }, + "storageClass": { + "description": "StorageClass used to store the data.", + "type": "string", + "default": "" + }, "core": { "description": "Core API server configuration.", "type": "object", @@ -56,125 +66,6 @@ } } }, - "database": { - "description": "PostgreSQL database configuration.", - "type": "object", - "default": {}, - "required": [ - "replicas", - "size" - ], - "properties": { - "replicas": { - "description": "Number of database instances.", - "type": "integer", - "default": 2 - }, - "size": { - "description": "Persistent Volume size for database storage.", - "default": "5Gi", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - } - } - }, - "host": { - "description": "Hostname for external access to Harbor (defaults to 'harbor' subdomain for the tenant host).", - "type": "string", - "default": "" - }, - "jobservice": { - "description": "Background job service configuration.", - "type": "object", - "default": {}, - "properties": { - "resources": { - "description": "Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.", - "type": "object", - "default": {}, - "properties": { - "cpu": { - "description": "Number of CPU cores allocated.", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - }, - "memory": { - "description": "Amount of memory allocated.", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - } - } - }, - "resourcesPreset": { - "description": "Default sizing preset used when `resources` is omitted.", - "type": "string", - "default": "nano", - "enum": [ - "nano", - "micro", - "small", - "medium", - "large", - "xlarge", - "2xlarge" - ] - } - } - }, - "redis": { - "description": "Redis cache configuration.", - "type": "object", - "default": {}, - "required": [ - "replicas", - "size" - ], - "properties": { - "replicas": { - "description": "Number of Redis replicas.", - "type": "integer", - "default": 2 - }, - "size": { - "description": "Persistent Volume size for cache storage.", - "default": "1Gi", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - } - } - }, "registry": { "description": "Container image registry configuration.", "type": "object", @@ -229,10 +120,59 @@ } } }, - "storageClass": { - "description": "StorageClass used to store the data.", - "type": "string", - "default": "" + "jobservice": { + "description": "Background job service configuration.", + "type": "object", + "default": {}, + "properties": { + "resources": { + "description": "Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.", + "type": "object", + "default": {}, + "properties": { + "cpu": { + "description": "Number of CPU cores allocated.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "memory": { + "description": "Amount of memory allocated.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + } + } + }, + "resourcesPreset": { + "description": "Default sizing preset used when `resources` is omitted.", + "type": "string", + "default": "nano", + "enum": [ + "nano", + "micro", + "small", + "medium", + "large", + "xlarge", + "2xlarge" + ] + } + } }, "trivy": { "description": "Trivy vulnerability scanner configuration.", @@ -310,6 +250,66 @@ "x-kubernetes-int-or-string": true } } + }, + "database": { + "description": "PostgreSQL database configuration.", + "type": "object", + "default": {}, + "required": [ + "replicas", + "size" + ], + "properties": { + "replicas": { + "description": "Number of database instances.", + "type": "integer", + "default": 2 + }, + "size": { + "description": "Persistent Volume size for database storage.", + "default": "5Gi", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + } + } + }, + "redis": { + "description": "Redis cache configuration.", + "type": "object", + "default": {}, + "required": [ + "replicas", + "size" + ], + "properties": { + "replicas": { + "description": "Number of Redis replicas.", + "type": "integer", + "default": 2 + }, + "size": { + "description": "Persistent Volume size for cache storage.", + "default": "1Gi", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + } + } } } -} \ No newline at end of file +} diff --git a/packages/apps/http-cache/Makefile b/packages/apps/http-cache/Makefile index 2cc11f87..899f0288 100644 --- a/packages/apps/http-cache/Makefile +++ b/packages/apps/http-cache/Makefile @@ -17,7 +17,7 @@ image-nginx: rm -f images/nginx-cache.json generate: - cozyvalues-gen -v values.yaml -s values.schema.json -r README.md + cozyvalues-gen -m 'httpcache' -v values.yaml -s values.schema.json -r README.md -g ../../../api/apps/v1alpha1/httpcache/types.go ../../../hack/update-crd.sh update: diff --git a/packages/apps/http-cache/images/nginx-cache.tag b/packages/apps/http-cache/images/nginx-cache.tag index 2d0e803a..b08a374e 100644 --- a/packages/apps/http-cache/images/nginx-cache.tag +++ b/packages/apps/http-cache/images/nginx-cache.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/nginx-cache:0.0.0@sha256:cb25e40cb665b8bbeee8cb1ec39da4c9a7452ef3f2f371912bbc0d1b1e2d40a8 +ghcr.io/cozystack/cozystack/nginx-cache:0.0.0@sha256:d397781152ab9123b11b8191d92eba7a0d2faa376aa2c15ddeb67842a9b59bab diff --git a/packages/apps/http-cache/templates/workloadmonitor.yaml b/packages/apps/http-cache/templates/workloadmonitor.yaml index 150d8bbe..a38a395a 100644 --- a/packages/apps/http-cache/templates/workloadmonitor.yaml +++ b/packages/apps/http-cache/templates/workloadmonitor.yaml @@ -3,6 +3,8 @@ apiVersion: cozystack.io/v1alpha1 kind: WorkloadMonitor metadata: name: {{ $.Release.Name }}-haproxy + labels: + workloads.cozystack.io/resource-preset: {{ .Values.haproxy.resourcesPreset | quote }} spec: replicas: {{ .Values.haproxy.replicas }} minReplicas: 1 @@ -16,6 +18,8 @@ apiVersion: cozystack.io/v1alpha1 kind: WorkloadMonitor metadata: name: {{ $.Release.Name }}-nginx + labels: + workloads.cozystack.io/resource-preset: {{ .Values.nginx.resourcesPreset | quote }} spec: replicas: {{ .Values.nginx.replicas }} minReplicas: 1 diff --git a/packages/apps/http-cache/values.schema.json b/packages/apps/http-cache/values.schema.json index 7fd1a37a..14609cc3 100644 --- a/packages/apps/http-cache/values.schema.json +++ b/packages/apps/http-cache/values.schema.json @@ -2,6 +2,30 @@ "title": "Chart Values", "type": "object", "properties": { + "size": { + "description": "Persistent Volume Claim size available for application data.", + "default": "10Gi", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "storageClass": { + "description": "StorageClass used to store the data.", + "type": "string", + "default": "" + }, + "external": { + "description": "Enable external access from outside the cluster.", + "type": "boolean", + "default": false + }, "endpoints": { "description": "Endpoints configuration, as a list of \u003cip:port\u003e.", "type": "array", @@ -10,11 +34,6 @@ "type": "string" } }, - "external": { - "description": "Enable external access from outside the cluster.", - "type": "boolean", - "default": false - }, "haproxy": { "description": "HAProxy configuration.", "type": "object", @@ -140,25 +159,6 @@ ] } } - }, - "size": { - "description": "Persistent Volume Claim size available for application data.", - "default": "10Gi", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - }, - "storageClass": { - "description": "StorageClass used to store the data.", - "type": "string", - "default": "" } } -} \ No newline at end of file +} diff --git a/packages/apps/kafka/Makefile b/packages/apps/kafka/Makefile index 2cffdfa7..c1084e86 100644 --- a/packages/apps/kafka/Makefile +++ b/packages/apps/kafka/Makefile @@ -2,5 +2,5 @@ include ../../../hack/package.mk PRESET_ENUM := ["nano","micro","small","medium","large","xlarge","2xlarge"] generate: - cozyvalues-gen -v values.yaml -s values.schema.json -r README.md + cozyvalues-gen -m 'kafka' -v values.yaml -s values.schema.json -r README.md -g ../../../api/apps/v1alpha1/kafka/types.go ../../../hack/update-crd.sh diff --git a/packages/apps/kafka/templates/workloadmonitor.yaml b/packages/apps/kafka/templates/workloadmonitor.yaml index 4b161b04..c31eb425 100644 --- a/packages/apps/kafka/templates/workloadmonitor.yaml +++ b/packages/apps/kafka/templates/workloadmonitor.yaml @@ -3,6 +3,8 @@ apiVersion: cozystack.io/v1alpha1 kind: WorkloadMonitor metadata: name: {{ $.Release.Name }} + labels: + workloads.cozystack.io/resource-preset: {{ .Values.kafka.resourcesPreset | quote }} spec: replicas: {{ .Values.replicas }} minReplicas: 1 @@ -19,6 +21,8 @@ apiVersion: cozystack.io/v1alpha1 kind: WorkloadMonitor metadata: name: {{ $.Release.Name }}-zookeeper + labels: + workloads.cozystack.io/resource-preset: {{ .Values.zookeeper.resourcesPreset | quote }} spec: replicas: {{ .Values.replicas }} minReplicas: 1 diff --git a/packages/apps/kafka/values.schema.json b/packages/apps/kafka/values.schema.json index 4b375b63..e3b6db12 100644 --- a/packages/apps/kafka/values.schema.json +++ b/packages/apps/kafka/values.schema.json @@ -7,6 +7,39 @@ "type": "boolean", "default": false }, + "topics": { + "description": "Topics configuration.", + "type": "array", + "default": [], + "items": { + "type": "object", + "required": [ + "config", + "name", + "partitions", + "replicas" + ], + "properties": { + "config": { + "description": "Topic configuration.", + "type": "object", + "x-kubernetes-preserve-unknown-fields": true + }, + "name": { + "description": "Topic name.", + "type": "string" + }, + "partitions": { + "description": "Number of partitions.", + "type": "integer" + }, + "replicas": { + "description": "Number of replicas.", + "type": "integer" + } + } + } + }, "kafka": { "description": "Kafka configuration.", "type": "object", @@ -91,39 +124,6 @@ } } }, - "topics": { - "description": "Topics configuration.", - "type": "array", - "default": [], - "items": { - "type": "object", - "required": [ - "config", - "name", - "partitions", - "replicas" - ], - "properties": { - "config": { - "description": "Topic configuration.", - "type": "object", - "x-kubernetes-preserve-unknown-fields": true - }, - "name": { - "description": "Topic name.", - "type": "string" - }, - "partitions": { - "description": "Number of partitions.", - "type": "integer" - }, - "replicas": { - "description": "Number of replicas.", - "type": "integer" - } - } - } - }, "zookeeper": { "description": "ZooKeeper configuration.", "type": "object", @@ -209,4 +209,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/apps/kubernetes/Makefile b/packages/apps/kubernetes/Makefile index e6ed7c6f..4fea42ef 100644 --- a/packages/apps/kubernetes/Makefile +++ b/packages/apps/kubernetes/Makefile @@ -1,11 +1,14 @@ -KUBERNETES_VERSION = v1.33 +KUBERNETES_VERSIONS = $(shell awk -F'"' '{print $$2}' files/versions.yaml) KUBERNETES_PKG_TAG = $(shell awk '$$1 == "version:" {print $$2}' Chart.yaml) include ../../../hack/common-envs.mk include ../../../hack/package.mk +test: + helm unittest . + generate: - cozyvalues-gen -v values.yaml -s values.schema.json -r README.md + cozyvalues-gen -m 'kubernetes' -v values.yaml -s values.schema.json -r README.md -g ../../../api/apps/v1alpha1/kubernetes/types.go ../../../hack/update-crd.sh update: @@ -15,17 +18,19 @@ update: image: image-ubuntu-container-disk image-kubevirt-cloud-provider image-kubevirt-csi-driver image-cluster-autoscaler image-ubuntu-container-disk: - docker buildx build images/ubuntu-container-disk \ - --build-arg KUBERNETES_VERSION=${KUBERNETES_VERSION} \ - --tag $(REGISTRY)/ubuntu-container-disk:$(call settag,$(KUBERNETES_VERSION)) \ - --tag $(REGISTRY)/ubuntu-container-disk:$(call settag,$(KUBERNETES_VERSION)-$(TAG)) \ - --cache-from type=registry,ref=$(REGISTRY)/ubuntu-container-disk:latest \ - --cache-to type=inline \ - --metadata-file images/ubuntu-container-disk.json \ - $(BUILDX_ARGS) - echo "$(REGISTRY)/ubuntu-container-disk:$(call settag,$(KUBERNETES_VERSION))@$$(yq e '."containerimage.digest"' images/ubuntu-container-disk.json -o json -r)" \ - > images/ubuntu-container-disk.tag - rm -f images/ubuntu-container-disk.json + $(foreach ver,$(KUBERNETES_VERSIONS), \ + docker buildx build images/ubuntu-container-disk \ + --build-arg KUBERNETES_VERSION=$(ver) \ + --tag $(REGISTRY)/ubuntu-container-disk:$(call settag,$(ver)) \ + --tag $(REGISTRY)/ubuntu-container-disk:$(call settag,$(ver)-$(TAG)) \ + --cache-from type=registry,ref=$(REGISTRY)/ubuntu-container-disk:$(call settag,$(ver)) \ + --cache-to type=inline \ + --metadata-file images/ubuntu-container-disk-$(ver).json \ + $(BUILDX_ARGS) && \ + echo "$(REGISTRY)/ubuntu-container-disk:$(call settag,$(ver))@$$(yq e '."containerimage.digest"' images/ubuntu-container-disk-$(ver).json -o json -r)" \ + > images/ubuntu-container-disk-$(ver).tag && \ + rm -f images/ubuntu-container-disk-$(ver).json; \ + ) image-kubevirt-cloud-provider: docker buildx build images/kubevirt-cloud-provider \ @@ -65,3 +70,4 @@ image-cluster-autoscaler: echo "$(REGISTRY)/cluster-autoscaler:$(call settag,$(KUBERNETES_PKG_TAG))@$$(yq e '."containerimage.digest"' images/cluster-autoscaler.json -o json -r)" \ > images/cluster-autoscaler.tag rm -f images/cluster-autoscaler.json + diff --git a/packages/apps/kubernetes/README.md b/packages/apps/kubernetes/README.md index b2fe102f..bed1117a 100644 --- a/packages/apps/kubernetes/README.md +++ b/packages/apps/kubernetes/README.md @@ -104,7 +104,7 @@ See the reference for components utilized in this service: | `nodeGroups[name].resources.memory` | Memory (RAM) available. | `quantity` | `""` | | `nodeGroups[name].gpus` | List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM). | `[]object` | `[]` | | `nodeGroups[name].gpus[i].name` | Name of GPU, such as "nvidia.com/AD102GL_L40S". | `string` | `""` | -| `version` | Kubernetes major.minor version to deploy | `string` | `v1.33` | +| `version` | Kubernetes major.minor version to deploy | `string` | `v1.35` | | `host` | External hostname for Kubernetes cluster. Defaults to `.` if empty. | `string` | `""` | @@ -128,6 +128,9 @@ See the reference for components utilized in this service: | `addons.gpuOperator` | NVIDIA GPU Operator. | `object` | `{}` | | `addons.gpuOperator.enabled` | Enable GPU Operator. | `bool` | `false` | | `addons.gpuOperator.valuesOverride` | Custom Helm values overrides. | `object` | `{}` | +| `addons.hami` | HAMi GPU virtualization middleware. | `object` | `{}` | +| `addons.hami.enabled` | Enable HAMi (requires GPU Operator). | `bool` | `false` | +| `addons.hami.valuesOverride` | Custom Helm values overrides. | `object` | `{}` | | `addons.fluxcd` | FluxCD GitOps operator. | `object` | `{}` | | `addons.fluxcd.enabled` | Enable FluxCD. | `bool` | `false` | | `addons.fluxcd.valuesOverride` | Custom Helm values overrides. | `object` | `{}` | @@ -145,31 +148,33 @@ See the reference for components utilized in this service: ### Kubernetes Control Plane Configuration -| Name | Description | Type | Value | -| --------------------------------------------------- | ------------------------------------------------ | ---------- | ------- | -| `controlPlane` | Kubernetes control-plane configuration. | `object` | `{}` | -| `controlPlane.replicas` | Number of control-plane replicas. | `int` | `2` | -| `controlPlane.apiServer` | API Server configuration. | `object` | `{}` | -| `controlPlane.apiServer.resources` | CPU and memory resources for API Server. | `object` | `{}` | -| `controlPlane.apiServer.resources.cpu` | CPU available. | `quantity` | `""` | -| `controlPlane.apiServer.resources.memory` | Memory (RAM) available. | `quantity` | `""` | -| `controlPlane.apiServer.resourcesPreset` | Preset if `resources` omitted. | `string` | `large` | -| `controlPlane.controllerManager` | Controller Manager configuration. | `object` | `{}` | -| `controlPlane.controllerManager.resources` | CPU and memory resources for Controller Manager. | `object` | `{}` | -| `controlPlane.controllerManager.resources.cpu` | CPU available. | `quantity` | `""` | -| `controlPlane.controllerManager.resources.memory` | Memory (RAM) available. | `quantity` | `""` | -| `controlPlane.controllerManager.resourcesPreset` | Preset if `resources` omitted. | `string` | `micro` | -| `controlPlane.scheduler` | Scheduler configuration. | `object` | `{}` | -| `controlPlane.scheduler.resources` | CPU and memory resources for Scheduler. | `object` | `{}` | -| `controlPlane.scheduler.resources.cpu` | CPU available. | `quantity` | `""` | -| `controlPlane.scheduler.resources.memory` | Memory (RAM) available. | `quantity` | `""` | -| `controlPlane.scheduler.resourcesPreset` | Preset if `resources` omitted. | `string` | `micro` | -| `controlPlane.konnectivity` | Konnectivity configuration. | `object` | `{}` | -| `controlPlane.konnectivity.server` | Konnectivity Server configuration. | `object` | `{}` | -| `controlPlane.konnectivity.server.resources` | CPU and memory resources for Konnectivity. | `object` | `{}` | -| `controlPlane.konnectivity.server.resources.cpu` | CPU available. | `quantity` | `""` | -| `controlPlane.konnectivity.server.resources.memory` | Memory (RAM) available. | `quantity` | `""` | -| `controlPlane.konnectivity.server.resourcesPreset` | Preset if `resources` omitted. | `string` | `micro` | +| Name | Description | Type | Value | +| --------------------------------------------------- | --------------------------------------------------------------------------------------------- | ---------- | ------- | +| `controlPlane` | Kubernetes control-plane configuration. | `object` | `{}` | +| `controlPlane.replicas` | Number of control-plane replicas. | `int` | `2` | +| `controlPlane.apiServer` | API Server configuration. | `object` | `{}` | +| `controlPlane.apiServer.resources` | CPU and memory resources for API Server. | `object` | `{}` | +| `controlPlane.apiServer.resources.cpu` | CPU available. | `quantity` | `""` | +| `controlPlane.apiServer.resources.memory` | Memory (RAM) available. | `quantity` | `""` | +| `controlPlane.apiServer.resourcesPreset` | Preset if `resources` omitted. | `string` | `large` | +| `controlPlane.controllerManager` | Controller Manager configuration. | `object` | `{}` | +| `controlPlane.controllerManager.resources` | CPU and memory resources for Controller Manager. | `object` | `{}` | +| `controlPlane.controllerManager.resources.cpu` | CPU available. | `quantity` | `""` | +| `controlPlane.controllerManager.resources.memory` | Memory (RAM) available. | `quantity` | `""` | +| `controlPlane.controllerManager.resourcesPreset` | Preset if `resources` omitted. | `string` | `micro` | +| `controlPlane.scheduler` | Scheduler configuration. | `object` | `{}` | +| `controlPlane.scheduler.resources` | CPU and memory resources for Scheduler. | `object` | `{}` | +| `controlPlane.scheduler.resources.cpu` | CPU available. | `quantity` | `""` | +| `controlPlane.scheduler.resources.memory` | Memory (RAM) available. | `quantity` | `""` | +| `controlPlane.scheduler.resourcesPreset` | Preset if `resources` omitted. | `string` | `micro` | +| `controlPlane.konnectivity` | Konnectivity configuration. | `object` | `{}` | +| `controlPlane.konnectivity.server` | Konnectivity Server configuration. | `object` | `{}` | +| `controlPlane.konnectivity.server.resources` | CPU and memory resources for Konnectivity. | `object` | `{}` | +| `controlPlane.konnectivity.server.resources.cpu` | CPU available. | `quantity` | `""` | +| `controlPlane.konnectivity.server.resources.memory` | Memory (RAM) available. | `quantity` | `""` | +| `controlPlane.konnectivity.server.resourcesPreset` | Preset if `resources` omitted. | `string` | `micro` | +| `images` | Optional image overrides for air-gapped or rate-limited registries. | `object` | `{}` | +| `images.waitForKubeconfig` | Image used by the wait-for-kubeconfig init container. Empty falls back to images/busybox.tag. | `string` | `""` | ## Parameter examples and reference diff --git a/packages/apps/kubernetes/files/konnectivity-versions.yaml b/packages/apps/kubernetes/files/konnectivity-versions.yaml new file mode 100644 index 00000000..09ef4fa7 --- /dev/null +++ b/packages/apps/kubernetes/files/konnectivity-versions.yaml @@ -0,0 +1,4 @@ +# Konnectivity proxy version overrides per Kubernetes minor version. +# When empty or absent, Kamaji auto-derives v0.{minor}.0 from the Kubernetes version. +# Add entries here only when the auto-derived image tag does not exist in the registry. +"v1.35": "v0.34.0" diff --git a/packages/apps/kubernetes/files/versions.yaml b/packages/apps/kubernetes/files/versions.yaml index 51a36034..58942b97 100644 --- a/packages/apps/kubernetes/files/versions.yaml +++ b/packages/apps/kubernetes/files/versions.yaml @@ -1,6 +1,6 @@ -"v1.33": "v1.33.0" -"v1.32": "v1.32.10" +"v1.35": "v1.35.1" +"v1.34": "v1.34.4" +"v1.33": "v1.33.8" +"v1.32": "v1.32.12" "v1.31": "v1.31.14" "v1.30": "v1.30.14" -"v1.29": "v1.29.15" -"v1.28": "v1.28.15" diff --git a/packages/apps/kubernetes/images/busybox.tag b/packages/apps/kubernetes/images/busybox.tag new file mode 100644 index 00000000..39de220a --- /dev/null +++ b/packages/apps/kubernetes/images/busybox.tag @@ -0,0 +1 @@ +docker.io/library/busybox:1.37.0@sha256:1487d0af5f52b4ba31c7e465126ee2123fe3f2305d638e7827681e7cf6c83d5e diff --git a/packages/apps/kubernetes/images/cluster-autoscaler.tag b/packages/apps/kubernetes/images/cluster-autoscaler.tag index c3d6ddef..d9fd9a52 100644 --- a/packages/apps/kubernetes/images/cluster-autoscaler.tag +++ b/packages/apps/kubernetes/images/cluster-autoscaler.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/cluster-autoscaler:0.0.0@sha256:7deeee117e7eec599cb453836ca95eadd131dfc8c875dc457ef29dc1433395e0 +ghcr.io/cozystack/cozystack/cluster-autoscaler:0.0.0@sha256:e9d0aa7e651b03a6713b380101a61832818a91b5f989da9754f58693898c4056 diff --git a/packages/apps/kubernetes/images/kubevirt-csi-driver.tag b/packages/apps/kubernetes/images/kubevirt-csi-driver.tag index d61f214a..00aa2caa 100644 --- a/packages/apps/kubernetes/images/kubevirt-csi-driver.tag +++ b/packages/apps/kubernetes/images/kubevirt-csi-driver.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:604561e23df1b8eb25c24cf73fd93c7aaa6d1e7c56affbbda5c6f0f83424e4b1 +ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:72154a97054e16cdf3dea6129d962b8d7e86b55cf9386095e8ac2ce7c8b69172 diff --git a/packages/apps/kubernetes/images/kubevirt-csi-driver/controller.go b/packages/apps/kubernetes/images/kubevirt-csi-driver/controller.go index 7192b95b..edddec09 100644 --- a/packages/apps/kubernetes/images/kubevirt-csi-driver/controller.go +++ b/packages/apps/kubernetes/images/kubevirt-csi-driver/controller.go @@ -317,11 +317,7 @@ func (w *WrappedControllerService) ControllerPublishVolume(ctx context.Context, "ownerReferences": []interface{}{vmiOwnerRef}, }, "spec": map[string]interface{}{ - "endpointSelector": map[string]interface{}{ - "matchLabels": map[string]interface{}{ - "kubevirt.io/vm": vmName, - }, - }, + "endpointSelector": buildEndpointSelector([]string{vmName}), "egress": []interface{}{ map[string]interface{}{ "toEndpoints": []interface{}{ @@ -441,6 +437,13 @@ func (w *WrappedControllerService) addCNPOwnerReference(ctx context.Context, nam if err := unstructured.SetNestedSlice(existing.Object, ownerRefs, "metadata", "ownerReferences"); err != nil { return status.Errorf(codes.Internal, "failed to set ownerReferences: %v", err) } + + // Rebuild endpointSelector to include all VMs + selector := buildEndpointSelector(vmNamesFromOwnerRefs(ownerRefs)) + if err := unstructured.SetNestedField(existing.Object, selector, "spec", "endpointSelector"); err != nil { + return status.Errorf(codes.Internal, "failed to set endpointSelector: %v", err) + } + if _, err := w.dynamicClient.Resource(ciliumNetworkPolicyGVR).Namespace(namespace).Update(ctx, existing, metav1.UpdateOptions{}); err != nil { return err } @@ -486,6 +489,13 @@ func (w *WrappedControllerService) removeCNPOwnerReference(ctx context.Context, if err := unstructured.SetNestedSlice(existing.Object, remaining, "metadata", "ownerReferences"); err != nil { return status.Errorf(codes.Internal, "failed to set ownerReferences: %v", err) } + + // Rebuild endpointSelector from remaining VMs + selector := buildEndpointSelector(vmNamesFromOwnerRefs(remaining)) + if err := unstructured.SetNestedField(existing.Object, selector, "spec", "endpointSelector"); err != nil { + return status.Errorf(codes.Internal, "failed to set endpointSelector: %v", err) + } + if _, err := w.dynamicClient.Resource(ciliumNetworkPolicyGVR).Namespace(namespace).Update(ctx, existing, metav1.UpdateOptions{}); err != nil { return err } @@ -494,6 +504,37 @@ func (w *WrappedControllerService) removeCNPOwnerReference(ctx context.Context, }) } +// buildEndpointSelector returns an endpointSelector using matchExpressions +// so that multiple VMs can be listed in a single selector. +func buildEndpointSelector(vmNames []string) map[string]interface{} { + values := make([]interface{}, len(vmNames)) + for i, name := range vmNames { + values[i] = name + } + return map[string]interface{}{ + "matchExpressions": []interface{}{ + map[string]interface{}{ + "key": "kubevirt.io/vm", + "operator": "In", + "values": values, + }, + }, + } +} + +// vmNamesFromOwnerRefs extracts VM names from ownerReferences. +func vmNamesFromOwnerRefs(ownerRefs []interface{}) []string { + var names []string + for _, ref := range ownerRefs { + if refMap, ok := ref.(map[string]interface{}); ok { + if name, ok := refMap["name"].(string); ok { + names = append(names, name) + } + } + } + return names +} + func hasRWXAccessMode(pvc *corev1.PersistentVolumeClaim) bool { for _, mode := range pvc.Spec.AccessModes { if mode == corev1.ReadWriteMany { diff --git a/packages/apps/kubernetes/images/kubevirt-csi-driver/node.go b/packages/apps/kubernetes/images/kubevirt-csi-driver/node.go index 5636a1d5..a367f27f 100644 --- a/packages/apps/kubernetes/images/kubevirt-csi-driver/node.go +++ b/packages/apps/kubernetes/images/kubevirt-csi-driver/node.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "os" + "path/filepath" csi "github.com/container-storage-interface/spec/lib/go/csi" "google.golang.org/grpc/codes" @@ -32,7 +33,8 @@ func (w *WrappedNodeService) NodeStageVolume(ctx context.Context, req *csi.NodeS return w.NodeService.NodeStageVolume(ctx, req) } -// NodePublishVolume for NFS volumes: mounts NFS at the target path. +// NodePublishVolume for NFS volumes: mounts the /data subdir of the NFS export +// at the target path, hiding internal artifacts (disk.img, lost+found). // For RWO volumes, delegates to upstream (mount block device). func (w *WrappedNodeService) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) { nfsExport := req.GetPublishContext()[nfsExportKey] @@ -60,13 +62,11 @@ func (w *WrappedNodeService) NodePublishVolume(ctx context.Context, req *csi.Nod } notMnt = true } - if !notMnt { klog.V(3).Infof("NFS volume %s already mounted at %s", req.GetVolumeId(), targetPath) return &csi.NodePublishVolumeResponse{}, nil } - source := fmt.Sprintf("%s:%s", host, path) mountOptions := []string{ "nfsvers=4.2", fmt.Sprintf("port=%s", port), @@ -75,9 +75,60 @@ func (w *WrappedNodeService) NodePublishVolume(ctx context.Context, req *csi.Nod mountOptions = append(mountOptions, "ro") } - klog.V(3).Infof("Mounting NFS %s at %s with options %v", source, targetPath, mountOptions) - if err := w.mounter.Mount(source, targetPath, "nfs", mountOptions); err != nil { - return nil, status.Errorf(codes.Internal, "NFS mount of %s at %s failed: %v", source, targetPath, err) + // Temp-mount the NFS root to ensure /data subdir exists and migrate any + // user files that were written before this fix (backward compatibility). + tmpMount, err := os.MkdirTemp("", fmt.Sprintf("nfs-init-%s-", req.GetVolumeId())) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to create temp mount dir: %v", err) + } + defer os.Remove(tmpMount) + + rootSource := fmt.Sprintf("%s:%s", host, path) + rootOpts := []string{"nfsvers=4.2", fmt.Sprintf("port=%s", port)} + if err := w.mounter.Mount(rootSource, tmpMount, "nfs", rootOpts); err != nil { + return nil, status.Errorf(codes.Internal, "NFS temp mount failed: %v", err) + } + defer func() { + if err := w.mounter.Unmount(tmpMount); err != nil { + klog.Warningf("Failed to unmount temp dir %s: %v", tmpMount, err) + } + }() + + dataDir := filepath.Join(tmpMount, "data") + if err := os.MkdirAll(dataDir, 0777); err != nil { + return nil, status.Errorf(codes.Internal, "failed to create /data subdir: %v", err) + } + + // Auto-migrate: move user files from root into /data (skip internal artifacts). + // Fail the publish if migration cannot complete to avoid hiding user data. + entries, err := os.ReadDir(tmpMount) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to read NFS root for migration (volume %s): %v", req.GetVolumeId(), err) + } + for _, entry := range entries { + name := entry.Name() + if name == "data" || name == "disk.img" || name == "lost+found" { + continue + } + src := filepath.Join(tmpMount, name) + dst := filepath.Join(dataDir, name) + if _, err := os.Stat(dst); err == nil { + continue // already exists in /data, skip + } + klog.Infof("Migrating %s to /data/%s for volume %s", name, name, req.GetVolumeId()) + if err := os.Rename(src, dst); err != nil { + if os.IsNotExist(err) { + continue // benign: concurrent publish already moved it + } + return nil, status.Errorf(codes.Internal, "failed to migrate %s for volume %s: %v", name, req.GetVolumeId(), err) + } + } + + // Mount only the /data subdir at the pod's target path. + dataSource := fmt.Sprintf("%s:%s/data", host, path) + klog.V(3).Infof("Mounting NFS %s at %s with options %v", dataSource, targetPath, mountOptions) + if err := w.mounter.Mount(dataSource, targetPath, "nfs", mountOptions); err != nil { + return nil, status.Errorf(codes.Internal, "NFS mount of %s at %s failed: %v", dataSource, targetPath, err) } return &csi.NodePublishVolumeResponse{}, nil diff --git a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.30.tag b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.30.tag new file mode 100644 index 00000000..f49fffe6 --- /dev/null +++ b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.30.tag @@ -0,0 +1 @@ +ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.30@sha256:f460ed1fd5a721aed423e6c3b8be0105d7f693cd86ad992d9c15fd4b27e58cec diff --git a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.31.tag b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.31.tag new file mode 100644 index 00000000..b5bdc79a --- /dev/null +++ b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.31.tag @@ -0,0 +1 @@ +ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.31@sha256:aaa2dfa8ee53ae26295f44d2491330f51412457bbe4aa6ba256297cc0bd8a0da diff --git a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.32.tag b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.32.tag new file mode 100644 index 00000000..ca285644 --- /dev/null +++ b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.32.tag @@ -0,0 +1 @@ +ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.32@sha256:7acb4728ef6b9c0ff3344bf486bb00f2a083f2098452344a362242235013db01 diff --git a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.33.tag b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.33.tag new file mode 100644 index 00000000..bb58690a --- /dev/null +++ b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.33.tag @@ -0,0 +1 @@ +ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.33@sha256:a2024339ab9edb980a96b43ad2744b7aa31afaef0983ddbc6a546068b4cfa87a diff --git a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.34.tag b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.34.tag new file mode 100644 index 00000000..36eed7a7 --- /dev/null +++ b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.34.tag @@ -0,0 +1 @@ +ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.34@sha256:97bcf946a5687889c6a421d14e5adece11f0978151b2165dc87b28f77f7607db diff --git a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.35.tag b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.35.tag new file mode 100644 index 00000000..faae83d2 --- /dev/null +++ b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.35.tag @@ -0,0 +1 @@ +ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.35@sha256:364c6d454891f1eb1a598fddb69cf328a14dbc451a8ac65812b038a7756da60a diff --git a/packages/apps/kubernetes/images/ubuntu-container-disk.tag b/packages/apps/kubernetes/images/ubuntu-container-disk.tag deleted file mode 100644 index 5b0830a6..00000000 --- a/packages/apps/kubernetes/images/ubuntu-container-disk.tag +++ /dev/null @@ -1 +0,0 @@ -ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.33@sha256:19ee4c76f0b3b7b40b97995ca78988ad8c82f6e9c75288d8b7b4b88a64f75d50 diff --git a/packages/apps/kubernetes/templates/_helpers.tpl b/packages/apps/kubernetes/templates/_helpers.tpl index 36c06b64..40a8ae7a 100644 --- a/packages/apps/kubernetes/templates/_helpers.tpl +++ b/packages/apps/kubernetes/templates/_helpers.tpl @@ -49,3 +49,52 @@ Selector labels app.kubernetes.io/name: {{ include "kubernetes.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} + +{{/* +wait-for-kubeconfig init container shared by the control-plane-side +Deployments (cluster-autoscaler, kccm, kcsi-controller) that mount the +*-admin-kubeconfig Secret provisioned asynchronously by Kamaji. The +Secret volume is declared optional so kubelet does not FailedMount while +Kamaji is still bootstrapping; this container polls the mounted path and +exits only when super-admin.svc appears, which happens after kubelet's +optional-Secret refresh cycle. + +The 10m deadline stays strictly below the 15m HelmRelease +Install.Timeout set by cozystack-api for the Kubernetes kind (via the +release.cozystack.io/helm-install-timeout annotation) so the +CrashLoopBackOff surfaces before flux remediation fires and uninstalls +the Cluster CR. + +The default image lives in images/busybox.tag and points directly at +docker.io by digest (not mirrored to ghcr.io like the other .tag files +here): the payload is a one-shot sh loop and the digest pin makes the +pull immutable. Operators in air-gapped or rate-limited environments +can override it via .Values.images.waitForKubeconfig (any registry +reference kubelet can pull). When the value is empty the chart falls +back to the bundled digest pin, preserving the prior default. + +Call site owns the surrounding volumes block; the kubeconfig volume +must exist on the pod and mount at /etc/kubernetes/kubeconfig. +*/}} +{{- define "kubernetes.waitForAdminKubeconfig" -}} +- name: wait-for-kubeconfig + image: "{{ default (.Files.Get "images/busybox.tag" | trim) .Values.images.waitForKubeconfig }}" + command: + - sh + - -c + - | + set -eu + deadline=$(( $(date +%s) + 600 )) + until [ -s /etc/kubernetes/kubeconfig/super-admin.svc ]; do + if [ "$(date +%s)" -ge "$deadline" ]; then + echo "admin kubeconfig was not provisioned within 10m; exiting so the pod goes CrashLoopBackOff and surfaces in dashboards" >&2 + exit 1 + fi + echo "waiting for admin kubeconfig (provisioned by Kamaji, visible after kubelet Secret refresh)..." + sleep 5 + done + volumeMounts: + - name: kubeconfig + mountPath: /etc/kubernetes/kubeconfig + readOnly: true +{{- end }} diff --git a/packages/apps/kubernetes/templates/_versions.tpl b/packages/apps/kubernetes/templates/_versions.tpl index fcd4cee1..403caff1 100644 --- a/packages/apps/kubernetes/templates/_versions.tpl +++ b/packages/apps/kubernetes/templates/_versions.tpl @@ -5,3 +5,10 @@ {{- end }} {{- index $versionMap .Values.version }} {{- end }} + +{{- define "kubernetes.konnectivityVersion" }} +{{- $konnVersionMap := .Files.Get "files/konnectivity-versions.yaml" | fromYaml }} +{{- if hasKey $konnVersionMap .Values.version }} +{{- index $konnVersionMap .Values.version }} +{{- end }} +{{- end }} diff --git a/packages/apps/kubernetes/templates/cluster-autoscaler/deployment.yaml b/packages/apps/kubernetes/templates/cluster-autoscaler/deployment.yaml index a00e0155..298d86db 100644 --- a/packages/apps/kubernetes/templates/cluster-autoscaler/deployment.yaml +++ b/packages/apps/kubernetes/templates/cluster-autoscaler/deployment.yaml @@ -1,3 +1,14 @@ +{{- /* + Gate the control-plane-side workloads on the parent tenant having an etcd + DataStore. Without it no KamajiControlPlane is ever created, Kamaji never + provisions -admin-kubeconfig, and rendering these Deployments would cause + the wait-for-kubeconfig init to CrashLoopBackOff indefinitely, consuming + the parent HelmRelease install timeout and triggering the very uninstall + remediation cycle this chart is supposed to avoid. Rendering them only + when $etcd is set keeps the HelmRelease Ready while flux retries on its + interval and picks up the DataStore as soon as the Tenant chart finishes. +*/}} +{{- if .Values._namespace.etcd }} --- apiVersion: apps/v1 kind: Deployment @@ -23,6 +34,8 @@ spec: - key: node-role.kubernetes.io/control-plane operator: Exists effect: "NoSchedule" + initContainers: + {{- include "kubernetes.waitForAdminKubeconfig" $ | nindent 6 }} containers: - image: "{{ $.Files.Get "images/cluster-autoscaler.tag" | trim }}" name: cluster-autoscaler @@ -56,6 +69,7 @@ spec: name: cloud-config - secret: secretName: {{ .Release.Name }}-admin-kubeconfig + optional: true name: kubeconfig serviceAccountName: {{ .Release.Name }}-cluster-autoscaler terminationGracePeriodSeconds: 10 @@ -105,3 +119,4 @@ rules: - list - update - watch +{{- end }} diff --git a/packages/apps/kubernetes/templates/cluster.yaml b/packages/apps/kubernetes/templates/cluster.yaml index 3d9c854a..e1494e64 100644 --- a/packages/apps/kubernetes/templates/cluster.yaml +++ b/packages/apps/kubernetes/templates/cluster.yaml @@ -1,4 +1,15 @@ {{- $etcd := .Values._namespace.etcd }} +{{- /* + When $etcd is empty, the parent Tenant application has not populated + _namespace.etcd in cozystack-values yet - either the operator forgot to + set etcd: true on an ancestor Tenant, or the Tenant HelmRelease is still + reconciling. Either way, rendering a KamajiControlPlane with an empty + dataStoreName would be rejected by Kamaji's admission webhook and the + HelmRelease would fail to install, triggering remediation. Instead, emit + a single ConfigMap as a user-visible status beacon and skip the rest so + flux marks the HelmRelease Ready and retries its 5m reconcile loop until + the Tenant chart catches up. +*/}} {{- $ingress := .Values._namespace.ingress }} {{- $host := .Values._namespace.host }} {{- $kubevirtmachinetemplateNames := list }} @@ -70,11 +81,13 @@ spec: memory: guest: {{ .group.resources.memory }} {{- end }} + resources: + {{- include "cozy-lib.resources.sanitize" (list (dict "ephemeral-storage" .group.ephemeralStorage) $) | nindent 14 }} evictionStrategy: External volumes: - name: system containerDisk: - image: "{{ $.Files.Get "images/ubuntu-container-disk.tag" | trim }}" + image: "{{ $.Files.Get (printf "images/ubuntu-container-disk-%s.tag" $.Values.version) | trim }}" - name: ephemeral emptyDisk: capacity: {{ .group.ephemeralStorage | default "20Gi" }} @@ -82,6 +95,26 @@ spec: - name: default pod: {} {{- end }} +{{- if not $etcd }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Release.Name }}-awaiting-etcd + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/name: kubernetes + app.kubernetes.io/instance: {{ .Release.Name }} +data: + status: "awaiting-etcd" + message: | + No DataStore is available for this tenant Kubernetes cluster. The parent + Tenant application has not populated _namespace.etcd. Set spec.etcd: true + on an ancestor Tenant (usually tenant-root) and wait for its HelmRelease + to reconcile - this HelmRelease will pick up the DataStore on its next + 5m reconcile loop and provision the cluster. +{{- else }} --- apiVersion: cluster.x-k8s.io/v1beta1 kind: Cluster @@ -126,8 +159,16 @@ spec: dataStoreName: "{{ $etcd }}" addons: konnectivity: + {{- $konnVersion := include "kubernetes.konnectivityVersion" $ | trim }} + {{- if $konnVersion }} + agent: + version: {{ $konnVersion }} + {{- end }} server: port: 8132 + {{- if $konnVersion }} + version: {{ $konnVersion }} + {{- end }} resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.controlPlane.konnectivity.server.resourcesPreset .Values.controlPlane.konnectivity.server.resources $) | nindent 10 }} kubelet: cgroupfs: systemd @@ -241,6 +282,9 @@ spec: joinConfiguration: nodeRegistration: kubeletExtraArgs: {} + # Ignore this for 1.31 + ignorePreflightErrors: + - FileExisting-conntrack discovery: bootstrapToken: apiServerEndpoint: {{ $.Release.Name }}.{{ $.Release.Namespace }}.svc:6443 @@ -391,3 +435,4 @@ metadata: spec: {{- .spec | toYaml | nindent 2 }} {{- end }} +{{- end }} diff --git a/packages/apps/kubernetes/templates/csi/deploy.yaml b/packages/apps/kubernetes/templates/csi/deploy.yaml index 938b6d67..de62104c 100644 --- a/packages/apps/kubernetes/templates/csi/deploy.yaml +++ b/packages/apps/kubernetes/templates/csi/deploy.yaml @@ -1,3 +1,4 @@ +{{- if .Values._namespace.etcd }} kind: Deployment apiVersion: apps/v1 metadata: @@ -24,6 +25,8 @@ spec: - key: node-role.kubernetes.io/control-plane operator: Exists effect: "NoSchedule" + initContainers: + {{- include "kubernetes.waitForAdminKubeconfig" $ | nindent 6 }} containers: - name: csi-driver imagePullPolicy: Always @@ -234,4 +237,6 @@ spec: emptyDir: {} - secret: secretName: {{ .Release.Name }}-admin-kubeconfig + optional: true name: kubeconfig +{{- end }} diff --git a/packages/apps/kubernetes/templates/helmreleases/cert-manager-crds.yaml b/packages/apps/kubernetes/templates/helmreleases/cert-manager-crds.yaml index be07a8b9..fd9dac7e 100644 --- a/packages/apps/kubernetes/templates/helmreleases/cert-manager-crds.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/cert-manager-crds.yaml @@ -1,4 +1,4 @@ -{{- if .Values.addons.certManager.enabled }} +{{- if and .Values.addons.certManager.enabled .Values._namespace.etcd }} apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: diff --git a/packages/apps/kubernetes/templates/helmreleases/cert-manager.yaml b/packages/apps/kubernetes/templates/helmreleases/cert-manager.yaml index 6857581a..700b666e 100644 --- a/packages/apps/kubernetes/templates/helmreleases/cert-manager.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/cert-manager.yaml @@ -8,7 +8,7 @@ cert-manager: {{- end }} {{- end }} -{{- if .Values.addons.certManager.enabled }} +{{- if and .Values.addons.certManager.enabled .Values._namespace.etcd }} apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: diff --git a/packages/apps/kubernetes/templates/helmreleases/cilium.yaml b/packages/apps/kubernetes/templates/helmreleases/cilium.yaml index 64027e94..d8c90cbf 100644 --- a/packages/apps/kubernetes/templates/helmreleases/cilium.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/cilium.yaml @@ -3,6 +3,7 @@ cilium: k8sServiceHost: {{ .Release.Name }}.{{ .Release.Namespace }}.svc k8sServicePort: 6443 routingMode: tunnel + MTU: 1350 enableIPv4Masquerade: true ipv4NativeRoutingCIDR: "" {{- if $.Values.addons.gatewayAPI.enabled }} @@ -13,6 +14,7 @@ cilium: {{- end }} {{- end }} +{{- if .Values._namespace.etcd }} apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: @@ -54,3 +56,4 @@ spec: - name: {{ .Release.Name }}-gateway-api-crds namespace: {{ .Release.Namespace }} {{- end }} +{{- end }} diff --git a/packages/apps/kubernetes/templates/helmreleases/coredns.yaml b/packages/apps/kubernetes/templates/helmreleases/coredns.yaml index bdb6c682..0711a51d 100644 --- a/packages/apps/kubernetes/templates/helmreleases/coredns.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/coredns.yaml @@ -4,6 +4,7 @@ coredns: clusterIP: "10.95.0.10" {{- end }} +{{- if .Values._namespace.etcd }} apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: @@ -42,3 +43,4 @@ spec: {{- end }} - name: {{ .Release.Name }}-cilium namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/packages/apps/kubernetes/templates/helmreleases/csi.yaml b/packages/apps/kubernetes/templates/helmreleases/csi.yaml index dd2c69a6..109d78e1 100644 --- a/packages/apps/kubernetes/templates/helmreleases/csi.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/csi.yaml @@ -1,3 +1,4 @@ +{{- if .Values._namespace.etcd }} apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: @@ -39,3 +40,4 @@ spec: - name: {{ .Release.Name }} namespace: {{ .Release.Namespace }} {{- end }} +{{- end }} diff --git a/packages/apps/kubernetes/templates/helmreleases/fluxcd.yaml b/packages/apps/kubernetes/templates/helmreleases/fluxcd.yaml index 76499dfe..25fff01c 100644 --- a/packages/apps/kubernetes/templates/helmreleases/fluxcd.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/fluxcd.yaml @@ -1,4 +1,4 @@ -{{- if .Values.addons.fluxcd.enabled }} +{{- if and .Values.addons.fluxcd.enabled .Values._namespace.etcd }} apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: diff --git a/packages/apps/kubernetes/templates/helmreleases/gateway-api-crds.yaml b/packages/apps/kubernetes/templates/helmreleases/gateway-api-crds.yaml index 2bcc8d4d..b4172ed1 100644 --- a/packages/apps/kubernetes/templates/helmreleases/gateway-api-crds.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/gateway-api-crds.yaml @@ -1,4 +1,4 @@ -{{- if $.Values.addons.gatewayAPI.enabled }} +{{- if and $.Values.addons.gatewayAPI.enabled $.Values._namespace.etcd }} apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: diff --git a/packages/apps/kubernetes/templates/helmreleases/gpu-operator.yaml b/packages/apps/kubernetes/templates/helmreleases/gpu-operator.yaml index 5ef48912..655dc868 100644 --- a/packages/apps/kubernetes/templates/helmreleases/gpu-operator.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/gpu-operator.yaml @@ -1,4 +1,12 @@ -{{- if .Values.addons.gpuOperator.enabled }} +{{- define "cozystack.defaultGpuOperatorValues" -}} +{{- if .Values.addons.hami.enabled }} +gpu-operator: + devicePlugin: + enabled: false +{{- end }} +{{- end }} + +{{- if and .Values.addons.gpuOperator.enabled .Values._namespace.etcd }} apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: @@ -29,9 +37,12 @@ spec: force: true remediation: retries: -1 - {{- with .Values.addons.gpuOperator.valuesOverride }} + {{- $defaults := fromYaml (include "cozystack.defaultGpuOperatorValues" .) }} + {{- $overrides := deepCopy (default (dict) .Values.addons.gpuOperator.valuesOverride) }} + {{- $merged := mergeOverwrite (default (dict) $defaults) $overrides }} + {{- if $merged }} values: - {{- toYaml . | nindent 4 }} + {{- toYaml $merged | nindent 4 }} {{- end }} dependsOn: diff --git a/packages/apps/kubernetes/templates/helmreleases/hami.yaml b/packages/apps/kubernetes/templates/helmreleases/hami.yaml new file mode 100644 index 00000000..f1538c7c --- /dev/null +++ b/packages/apps/kubernetes/templates/helmreleases/hami.yaml @@ -0,0 +1,49 @@ +{{- if and .Values.addons.hami.enabled .Values._namespace.etcd }} +{{- if not .Values.addons.gpuOperator.enabled }} +{{- fail "addons.hami requires addons.gpuOperator to be enabled" }} +{{- end }} +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: {{ .Release.Name }}-hami + labels: + cozystack.io/repository: system + cozystack.io/target-cluster-name: {{ .Release.Name }} + sharding.fluxcd.io/key: tenants +spec: + releaseName: hami + chartRef: + kind: ExternalArtifact + name: cozystack-kubernetes-application-kubevirt-kubernetes-hami + namespace: cozy-system + kubeConfig: + secretRef: + name: {{ .Release.Name }}-admin-kubeconfig + key: super-admin.svc + targetNamespace: cozy-hami + storageNamespace: cozy-hami + interval: 5m + timeout: 10m + install: + createNamespace: true + remediation: + retries: -1 + upgrade: + force: true + remediation: + retries: -1 + {{- with .Values.addons.hami.valuesOverride }} + values: + {{- toYaml . | nindent 4 }} + {{- end }} + + dependsOn: + {{- if lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" .Release.Namespace .Release.Name }} + - name: {{ .Release.Name }} + namespace: {{ .Release.Namespace }} + {{- end }} + - name: {{ .Release.Name }}-cilium + namespace: {{ .Release.Namespace }} + - name: {{ .Release.Name }}-gpu-operator + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/packages/apps/kubernetes/templates/helmreleases/ingress-nginx.yaml b/packages/apps/kubernetes/templates/helmreleases/ingress-nginx.yaml index 5cafff90..6e2183d3 100644 --- a/packages/apps/kubernetes/templates/helmreleases/ingress-nginx.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/ingress-nginx.yaml @@ -20,7 +20,7 @@ ingress-nginx: node-role.kubernetes.io/ingress-nginx: "" {{- end }} -{{- if .Values.addons.ingressNginx.enabled }} +{{- if and .Values.addons.ingressNginx.enabled .Values._namespace.etcd }} apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: diff --git a/packages/apps/kubernetes/templates/helmreleases/metrics-server.yaml b/packages/apps/kubernetes/templates/helmreleases/metrics-server.yaml index 3cc81a14..3e6f9660 100644 --- a/packages/apps/kubernetes/templates/helmreleases/metrics-server.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/metrics-server.yaml @@ -1,3 +1,4 @@ +{{- if .Values._namespace.etcd }} apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: @@ -36,3 +37,4 @@ spec: namespace: {{ .Release.Namespace }} - name: {{ .Release.Name }}-prometheus-operator-crds namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/packages/apps/kubernetes/templates/helmreleases/monitoring-agents.yaml b/packages/apps/kubernetes/templates/helmreleases/monitoring-agents.yaml index ff54b6e2..a811f7dd 100644 --- a/packages/apps/kubernetes/templates/helmreleases/monitoring-agents.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/monitoring-agents.yaml @@ -1,5 +1,6 @@ {{- $targetTenant := .Values._namespace.monitoring }} -{{- if .Values.addons.monitoringAgents.enabled }} +{{- $clusterDomain := (index .Values._cluster "cluster-domain") | default "cozy.local" }} +{{- if and .Values.addons.monitoringAgents.enabled .Values._namespace.etcd }} apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: @@ -49,7 +50,7 @@ spec: cluster: {{ .Release.Name }} tenant: {{ .Release.Namespace }} remoteWrite: - url: http://vminsert-shortterm.{{ $targetTenant }}.svc:8480/insert/0/prometheus + url: http://vminsert-shortterm.{{ $targetTenant }}.svc.{{ $clusterDomain }}:8480/insert/0/prometheus fluent-bit: readinessProbe: httpGet: @@ -72,8 +73,8 @@ spec: [OUTPUT] Name http Match kube.* - Host vlogs-generic.{{ $targetTenant }}.svc - port 9428 + Host vlinsert-generic.{{ $targetTenant }}.svc.{{ $clusterDomain }} + port 9481 compress gzip uri /insert/jsonline?_stream_fields=stream,kubernetes_pod_name,kubernetes_container_name,kubernetes_namespace_name&_msg_field=log&_time_field=date format json_lines diff --git a/packages/apps/kubernetes/templates/helmreleases/prometheus-operator-crds.yaml b/packages/apps/kubernetes/templates/helmreleases/prometheus-operator-crds.yaml index 600a7994..3038a058 100644 --- a/packages/apps/kubernetes/templates/helmreleases/prometheus-operator-crds.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/prometheus-operator-crds.yaml @@ -1,3 +1,4 @@ +{{- if .Values._namespace.etcd }} apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: @@ -31,3 +32,4 @@ spec: - name: {{ .Release.Name }} namespace: {{ .Release.Namespace }} {{- end }} +{{- end }} diff --git a/packages/apps/kubernetes/templates/helmreleases/velero.yaml b/packages/apps/kubernetes/templates/helmreleases/velero.yaml index ad236d53..781b9c49 100644 --- a/packages/apps/kubernetes/templates/helmreleases/velero.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/velero.yaml @@ -1,4 +1,4 @@ -{{- if .Values.addons.velero.enabled }} +{{- if and .Values.addons.velero.enabled .Values._namespace.etcd }} apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: diff --git a/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler-crds.yaml b/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler-crds.yaml index a3b7a9b4..55a5faac 100644 --- a/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler-crds.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler-crds.yaml @@ -1,4 +1,4 @@ -{{- if .Values.addons.monitoringAgents.enabled }} +{{- if and .Values.addons.monitoringAgents.enabled .Values._namespace.etcd }} apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: diff --git a/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler.yaml b/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler.yaml index 178df3e3..74fb5a39 100644 --- a/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler.yaml @@ -24,7 +24,7 @@ vertical-pod-autoscaler: memory: 1600Mi {{- end }} -{{- if .Values.addons.monitoringAgents.enabled }} +{{- if and .Values.addons.monitoringAgents.enabled .Values._namespace.etcd }} apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: diff --git a/packages/apps/kubernetes/templates/helmreleases/victoria-metrics-operator.yaml b/packages/apps/kubernetes/templates/helmreleases/victoria-metrics-operator.yaml index 99744277..7302f8f5 100644 --- a/packages/apps/kubernetes/templates/helmreleases/victoria-metrics-operator.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/victoria-metrics-operator.yaml @@ -1,4 +1,4 @@ -{{- if .Values.addons.monitoringAgents.enabled }} +{{- if and .Values.addons.monitoringAgents.enabled .Values._namespace.etcd }} apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: diff --git a/packages/apps/kubernetes/templates/helmreleases/volumesnapshot-crd.yaml b/packages/apps/kubernetes/templates/helmreleases/volumesnapshot-crd.yaml index 025f01b7..d50fd93c 100644 --- a/packages/apps/kubernetes/templates/helmreleases/volumesnapshot-crd.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/volumesnapshot-crd.yaml @@ -1,3 +1,4 @@ +{{- if .Values._namespace.etcd }} apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: @@ -33,3 +34,4 @@ spec: - name: {{ .Release.Name }} namespace: {{ .Release.Namespace }} {{- end }} +{{- end }} diff --git a/packages/apps/kubernetes/templates/kccm/manager.yaml b/packages/apps/kubernetes/templates/kccm/manager.yaml index 81426d4e..bd9e2798 100644 --- a/packages/apps/kubernetes/templates/kccm/manager.yaml +++ b/packages/apps/kubernetes/templates/kccm/manager.yaml @@ -1,3 +1,4 @@ +{{- if .Values._namespace.etcd }} apiVersion: apps/v1 kind: Deployment metadata: @@ -22,6 +23,8 @@ spec: - key: node-role.kubernetes.io/control-plane operator: Exists effect: "NoSchedule" + initContainers: + {{- include "kubernetes.waitForAdminKubeconfig" $ | nindent 6 }} containers: - name: kubevirt-cloud-controller-manager args: @@ -55,5 +58,7 @@ spec: name: cloud-config - secret: secretName: {{ .Release.Name }}-admin-kubeconfig + optional: true name: kubeconfig serviceAccountName: {{ .Release.Name }}-kccm +{{- end }} diff --git a/packages/apps/kubernetes/tests/admin_kubeconfig_wait_test.yaml b/packages/apps/kubernetes/tests/admin_kubeconfig_wait_test.yaml new file mode 100644 index 00000000..507e00af --- /dev/null +++ b/packages/apps/kubernetes/tests/admin_kubeconfig_wait_test.yaml @@ -0,0 +1,155 @@ +suite: admin-kubeconfig wait guards + +release: + name: test + namespace: tenant-root + +values: + - values-ci.yaml + +tests: + - it: cluster-autoscaler mounts admin-kubeconfig as optional + template: templates/cluster-autoscaler/deployment.yaml + documentSelector: + path: kind + value: Deployment + asserts: + - equal: + path: spec.template.spec.volumes[?(@.name=="kubeconfig")].secret.secretName + value: test-admin-kubeconfig + - equal: + path: spec.template.spec.volumes[?(@.name=="kubeconfig")].secret.optional + value: true + + - it: cluster-autoscaler waits for admin-kubeconfig via initContainer + template: templates/cluster-autoscaler/deployment.yaml + documentSelector: + path: kind + value: Deployment + asserts: + - equal: + path: spec.template.spec.initContainers[0].name + value: wait-for-kubeconfig + - contains: + path: spec.template.spec.initContainers[0].volumeMounts + content: + name: kubeconfig + mountPath: /etc/kubernetes/kubeconfig + readOnly: true + + - it: kccm mounts admin-kubeconfig as optional + template: templates/kccm/manager.yaml + documentSelector: + path: kind + value: Deployment + asserts: + - equal: + path: spec.template.spec.volumes[?(@.name=="kubeconfig")].secret.secretName + value: test-admin-kubeconfig + - equal: + path: spec.template.spec.volumes[?(@.name=="kubeconfig")].secret.optional + value: true + + - it: kccm waits for admin-kubeconfig via initContainer + template: templates/kccm/manager.yaml + documentSelector: + path: kind + value: Deployment + asserts: + - equal: + path: spec.template.spec.initContainers[0].name + value: wait-for-kubeconfig + - contains: + path: spec.template.spec.initContainers[0].volumeMounts + content: + name: kubeconfig + mountPath: /etc/kubernetes/kubeconfig + readOnly: true + + - it: csi controller mounts admin-kubeconfig as optional + template: templates/csi/deploy.yaml + documentSelector: + path: kind + value: Deployment + asserts: + - equal: + path: spec.template.spec.volumes[?(@.name=="kubeconfig")].secret.secretName + value: test-admin-kubeconfig + - equal: + path: spec.template.spec.volumes[?(@.name=="kubeconfig")].secret.optional + value: true + + - it: csi controller waits for admin-kubeconfig via initContainer + template: templates/csi/deploy.yaml + documentSelector: + path: kind + value: Deployment + asserts: + - equal: + path: spec.template.spec.initContainers[0].name + value: wait-for-kubeconfig + - contains: + path: spec.template.spec.initContainers[0].volumeMounts + content: + name: kubeconfig + mountPath: /etc/kubernetes/kubeconfig + readOnly: true + + - it: wait-for-kubeconfig defaults to bundled busybox digest pin + template: templates/cluster-autoscaler/deployment.yaml + documentSelector: + path: kind + value: Deployment + asserts: + - matchRegex: + path: spec.template.spec.initContainers[0].image + pattern: '^docker\.io/library/busybox:[^@]+@sha256:[0-9a-f]{64}$' + + - it: wait-for-kubeconfig honours images.waitForKubeconfig override + template: templates/cluster-autoscaler/deployment.yaml + documentSelector: + path: kind + value: Deployment + set: + images: + waitForKubeconfig: "registry.example.test/library/busybox:1.37.0@sha256:0000000000000000000000000000000000000000000000000000000000000000" + asserts: + - equal: + path: spec.template.spec.initContainers[0].image + value: "registry.example.test/library/busybox:1.37.0@sha256:0000000000000000000000000000000000000000000000000000000000000000" + + - it: cluster.yaml renders and wires dataStoreName when tenant has etcd + template: templates/cluster.yaml + documentSelector: + path: kind + value: KamajiControlPlane + asserts: + - equal: + path: spec.dataStoreName + value: tenant-root + + - it: cluster.yaml skips Cluster resources when tenant has no etcd DataStore + # Must NOT fail rendering - the parent Tenant chart populates + # _namespace.etcd asynchronously, so rendering failures here would cause + # flux install remediation on every cold bootstrap. Instead, emit only a + # ConfigMap status beacon so the HelmRelease reports Ready while flux + # retries on its interval until the DataStore appears. + template: templates/cluster.yaml + set: + _namespace: + etcd: "" + monitoring: "" + ingress: "" + seaweedfs: "" + host: "" + asserts: + - hasDocuments: + count: 1 + - isKind: + of: ConfigMap + - equal: + path: metadata.name + value: test-awaiting-etcd + - equal: + path: data.status + value: awaiting-etcd diff --git a/packages/apps/kubernetes/tests/gpu_operator_hami_test.yaml b/packages/apps/kubernetes/tests/gpu_operator_hami_test.yaml new file mode 100644 index 00000000..44528470 --- /dev/null +++ b/packages/apps/kubernetes/tests/gpu_operator_hami_test.yaml @@ -0,0 +1,99 @@ +suite: GPU Operator HelmRelease HAMi integration tests +templates: + - templates/helmreleases/gpu-operator.yaml +values: + - values-ci.yaml +tests: + - it: should disable devicePlugin when hami is enabled + set: + addons: + gpuOperator: + enabled: true + valuesOverride: {} + hami: + enabled: true + valuesOverride: {} + asserts: + - equal: + path: spec.values.gpu-operator.devicePlugin.enabled + value: false + + - it: should not have values when hami is disabled and no overrides + set: + addons: + gpuOperator: + enabled: true + valuesOverride: {} + hami: + enabled: false + valuesOverride: {} + asserts: + - notExists: + path: spec.values + + - it: should apply hami defaults when valuesOverride key is omitted + set: + addons: + gpuOperator: + enabled: true + hami: + enabled: true + asserts: + - equal: + path: spec.values.gpu-operator.devicePlugin.enabled + value: false + + - it: should allow user overrides to merge with hami defaults + set: + addons: + gpuOperator: + enabled: true + valuesOverride: + gpu-operator: + driver: + enabled: false + hami: + enabled: true + valuesOverride: {} + asserts: + - equal: + path: spec.values.gpu-operator.devicePlugin.enabled + value: false + - equal: + path: spec.values.gpu-operator.driver.enabled + value: false + + - it: should let user explicitly override devicePlugin.enabled to true with hami enabled + set: + addons: + gpuOperator: + enabled: true + valuesOverride: + gpu-operator: + devicePlugin: + enabled: true + driver: + enabled: false + hami: + enabled: true + valuesOverride: {} + asserts: + - equal: + path: spec.values.gpu-operator.devicePlugin.enabled + value: true + - equal: + path: spec.values.gpu-operator.driver.enabled + value: false + + - it: should not render when gpuOperator is disabled + set: + addons: + gpuOperator: + enabled: false + valuesOverride: {} + hami: + enabled: false + valuesOverride: {} + asserts: + - hasDocuments: + count: 0 diff --git a/packages/apps/kubernetes/tests/hami_test.yaml b/packages/apps/kubernetes/tests/hami_test.yaml new file mode 100644 index 00000000..27f7c75b --- /dev/null +++ b/packages/apps/kubernetes/tests/hami_test.yaml @@ -0,0 +1,153 @@ +suite: HAMi HelmRelease tests +templates: + - templates/helmreleases/hami.yaml +values: + - values-ci.yaml +tests: + - it: should not render when hami is disabled + set: + addons: + hami: + enabled: false + valuesOverride: {} + gpuOperator: + enabled: true + valuesOverride: {} + asserts: + - hasDocuments: + count: 0 + + - it: should render HelmRelease when hami is enabled + set: + addons: + hami: + enabled: true + valuesOverride: {} + gpuOperator: + enabled: true + valuesOverride: {} + asserts: + - hasDocuments: + count: 1 + - isKind: + of: HelmRelease + + - it: should fail when gpuOperator is not enabled + set: + addons: + hami: + enabled: true + valuesOverride: {} + gpuOperator: + enabled: false + valuesOverride: {} + asserts: + - failedTemplate: + errorMessage: "addons.hami requires addons.gpuOperator to be enabled" + + - it: should have correct metadata labels + set: + addons: + hami: + enabled: true + valuesOverride: {} + gpuOperator: + enabled: true + valuesOverride: {} + asserts: + - equal: + path: metadata.labels["cozystack.io/repository"] + value: system + - equal: + path: metadata.labels["sharding.fluxcd.io/key"] + value: tenants + + - it: should use ExternalArtifact chartRef + set: + addons: + hami: + enabled: true + valuesOverride: {} + gpuOperator: + enabled: true + valuesOverride: {} + asserts: + - equal: + path: spec.chartRef.kind + value: ExternalArtifact + - equal: + path: spec.chartRef.namespace + value: cozy-system + + - it: should target cozy-hami namespace + set: + addons: + hami: + enabled: true + valuesOverride: {} + gpuOperator: + enabled: true + valuesOverride: {} + asserts: + - equal: + path: spec.targetNamespace + value: cozy-hami + - equal: + path: spec.storageNamespace + value: cozy-hami + + - it: should depend on gpu-operator and cilium + release: + name: test + namespace: test-ns + set: + addons: + hami: + enabled: true + valuesOverride: {} + gpuOperator: + enabled: true + valuesOverride: {} + asserts: + - contains: + path: spec.dependsOn + content: + name: test-cilium + namespace: test-ns + - contains: + path: spec.dependsOn + content: + name: test-gpu-operator + namespace: test-ns + + - it: should not render spec.values when valuesOverride is empty + set: + addons: + hami: + enabled: true + valuesOverride: {} + gpuOperator: + enabled: true + valuesOverride: {} + asserts: + - hasDocuments: + count: 1 + - notExists: + path: spec.values + + - it: should pass through valuesOverride + set: + addons: + hami: + enabled: true + valuesOverride: + hami: + devicePlugin: + deviceSplitCount: 5 + gpuOperator: + enabled: true + valuesOverride: {} + asserts: + - equal: + path: spec.values.hami.devicePlugin.deviceSplitCount + value: 5 diff --git a/packages/apps/kubernetes/tests/values-ci-no-etcd.yaml b/packages/apps/kubernetes/tests/values-ci-no-etcd.yaml new file mode 100644 index 00000000..c7c8196f --- /dev/null +++ b/packages/apps/kubernetes/tests/values-ci-no-etcd.yaml @@ -0,0 +1,9 @@ +_namespace: + etcd: "" + monitoring: "" + ingress: "" + seaweedfs: "" + host: "" +_cluster: + cluster-domain: cozy.local +nodeGroups: null diff --git a/packages/apps/kubernetes/tests/values-ci.yaml b/packages/apps/kubernetes/tests/values-ci.yaml new file mode 100644 index 00000000..13365e8c --- /dev/null +++ b/packages/apps/kubernetes/tests/values-ci.yaml @@ -0,0 +1,9 @@ +_namespace: + etcd: tenant-root + monitoring: "" + ingress: "" + seaweedfs: "" + host: "" +_cluster: + cluster-domain: cozy.local +nodeGroups: null diff --git a/packages/apps/kubernetes/values.schema.json b/packages/apps/kubernetes/values.schema.json index 1f6c9361..a43deaec 100644 --- a/packages/apps/kubernetes/values.schema.json +++ b/packages/apps/kubernetes/values.schema.json @@ -2,6 +2,142 @@ "title": "Chart Values", "type": "object", "properties": { + "storageClass": { + "description": "StorageClass used to store the data.", + "type": "string", + "default": "replicated" + }, + "nodeGroups": { + "description": "Worker nodes configuration map.", + "type": "object", + "default": { + "md0": { + "ephemeralStorage": "20Gi", + "gpus": [], + "instanceType": "u1.medium", + "maxReplicas": 10, + "minReplicas": 0, + "resources": {}, + "roles": [ + "ingress-nginx" + ] + } + }, + "additionalProperties": { + "type": "object", + "required": [ + "ephemeralStorage", + "instanceType", + "maxReplicas", + "minReplicas", + "resources" + ], + "properties": { + "ephemeralStorage": { + "description": "Ephemeral storage size.", + "default": "20Gi", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "gpus": { + "description": "List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM).", + "type": "array", + "items": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "description": "Name of GPU, such as \"nvidia.com/AD102GL_L40S\".", + "type": "string" + } + } + } + }, + "instanceType": { + "description": "Virtual machine instance type.", + "type": "string", + "default": "u1.medium" + }, + "maxReplicas": { + "description": "Maximum number of replicas.", + "type": "integer", + "default": 10 + }, + "minReplicas": { + "description": "Minimum number of replicas.", + "type": "integer", + "default": 0 + }, + "resources": { + "description": "CPU and memory resources for each worker node.", + "type": "object", + "properties": { + "cpu": { + "description": "CPU available.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "memory": { + "description": "Memory (RAM) available.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + } + } + }, + "roles": { + "description": "List of node roles.", + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "version": { + "description": "Kubernetes major.minor version to deploy", + "type": "string", + "default": "v1.35", + "enum": [ + "v1.35", + "v1.34", + "v1.33", + "v1.32", + "v1.31", + "v1.30" + ] + }, + "host": { + "description": "External hostname for Kubernetes cluster. Defaults to `\u003ccluster-name\u003e.\u003ctenant-host\u003e` if empty.", + "type": "string", + "default": "" + }, "addons": { "description": "Cluster addons configuration.", "type": "object", @@ -13,6 +149,7 @@ "fluxcd", "gatewayAPI", "gpuOperator", + "hami", "ingressNginx", "monitoringAgents", "velero", @@ -132,6 +269,28 @@ } } }, + "hami": { + "description": "HAMi GPU virtualization middleware.", + "type": "object", + "default": {}, + "required": [ + "enabled", + "valuesOverride" + ], + "properties": { + "enabled": { + "description": "Enable HAMi (requires GPU Operator).", + "type": "boolean", + "default": false + }, + "valuesOverride": { + "description": "Custom Helm values overrides.", + "type": "object", + "default": {}, + "x-kubernetes-preserve-unknown-fields": true + } + } + }, "ingressNginx": { "description": "Ingress-NGINX controller.", "type": "object", @@ -495,141 +654,17 @@ } } }, - "host": { - "description": "External hostname for Kubernetes cluster. Defaults to `\u003ccluster-name\u003e.\u003ctenant-host\u003e` if empty.", - "type": "string", - "default": "" - }, - "nodeGroups": { - "description": "Worker nodes configuration map.", + "images": { + "description": "Optional image overrides for air-gapped or rate-limited registries.", "type": "object", - "default": { - "md0": { - "ephemeralStorage": "20Gi", - "gpus": [], - "instanceType": "u1.medium", - "maxReplicas": 10, - "minReplicas": 0, - "resources": {}, - "roles": [ - "ingress-nginx" - ] - } - }, - "additionalProperties": { - "type": "object", - "required": [ - "ephemeralStorage", - "instanceType", - "maxReplicas", - "minReplicas", - "resources" - ], - "properties": { - "ephemeralStorage": { - "description": "Ephemeral storage size.", - "default": "20Gi", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - }, - "gpus": { - "description": "List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM).", - "type": "array", - "items": { - "type": "object", - "required": [ - "name" - ], - "properties": { - "name": { - "description": "Name of GPU, such as \"nvidia.com/AD102GL_L40S\".", - "type": "string" - } - } - } - }, - "instanceType": { - "description": "Virtual machine instance type.", - "type": "string", - "default": "u1.medium" - }, - "maxReplicas": { - "description": "Maximum number of replicas.", - "type": "integer", - "default": 10 - }, - "minReplicas": { - "description": "Minimum number of replicas.", - "type": "integer", - "default": 0 - }, - "resources": { - "description": "CPU and memory resources for each worker node.", - "type": "object", - "properties": { - "cpu": { - "description": "CPU available.", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - }, - "memory": { - "description": "Memory (RAM) available.", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - } - } - }, - "roles": { - "description": "List of node roles.", - "type": "array", - "items": { - "type": "string" - } - } + "default": {}, + "properties": { + "waitForKubeconfig": { + "description": "Image used by the wait-for-kubeconfig init container. Empty falls back to images/busybox.tag.", + "type": "string", + "default": "" } } - }, - "storageClass": { - "description": "StorageClass used to store the data.", - "type": "string", - "default": "replicated" - }, - "version": { - "description": "Kubernetes major.minor version to deploy", - "type": "string", - "default": "v1.33", - "enum": [ - "v1.33", - "v1.32", - "v1.31", - "v1.30", - "v1.29", - "v1.28" - ] } } -} \ No newline at end of file +} diff --git a/packages/apps/kubernetes/values.yaml b/packages/apps/kubernetes/values.yaml index 7f15752f..d609476c 100644 --- a/packages/apps/kubernetes/values.yaml +++ b/packages/apps/kubernetes/values.yaml @@ -48,15 +48,15 @@ nodeGroups: ## ## @enum {string} Version +## @value v1.35 +## @value v1.34 ## @value v1.33 ## @value v1.32 ## @value v1.31 ## @value v1.30 -## @value v1.29 -## @value v1.28 ## @param {Version} version - Kubernetes major.minor version to deploy -version: "v1.33" +version: "v1.35" ## @param {string} host - External hostname for Kubernetes cluster. Defaults to `.` if empty. @@ -94,6 +94,10 @@ host: "" ## @field {bool} enabled - Enable FluxCD. ## @field {object} valuesOverride - Custom Helm values overrides. +## @typedef {struct} HAMiAddon - HAMi GPU virtualization middleware. +## @field {bool} enabled - Enable HAMi (requires GPU Operator). +## @field {object} valuesOverride - Custom Helm values overrides. + ## @typedef {struct} MonitoringAgentsAddon - Monitoring agents (Fluent Bit, VMAgents). ## @field {bool} enabled - Enable monitoring agents. ## @field {object} valuesOverride - Custom Helm values overrides. @@ -114,6 +118,7 @@ host: "" ## @field {GatewayAPIAddon} gatewayAPI - Gateway API addon. ## @field {IngressNginxAddon} ingressNginx - Ingress-NGINX controller. ## @field {GPUOperatorAddon} gpuOperator - NVIDIA GPU Operator. +## @field {HAMiAddon} hami - HAMi GPU virtualization middleware. ## @field {FluxCDAddon} fluxcd - FluxCD GitOps operator. ## @field {MonitoringAgentsAddon} monitoringAgents - Monitoring agents. ## @field {VerticalPodAutoscalerAddon} verticalPodAutoscaler - Vertical Pod Autoscaler. @@ -137,6 +142,9 @@ addons: gpuOperator: enabled: false valuesOverride: {} + hami: + enabled: false + valuesOverride: {} fluxcd: enabled: false valuesOverride: {} @@ -197,3 +205,10 @@ controlPlane: server: resources: {} resourcesPreset: "micro" + +## @typedef {struct} Images - Optional image overrides for chart-internal helpers. +## @field {string} [waitForKubeconfig] - Image used by the wait-for-kubeconfig init container. Empty falls back to images/busybox.tag. + +## @param {Images} images - Optional image overrides for air-gapped or rate-limited registries. +images: + waitForKubeconfig: "" diff --git a/packages/apps/mariadb/Makefile b/packages/apps/mariadb/Makefile index e8f02703..9833f302 100644 --- a/packages/apps/mariadb/Makefile +++ b/packages/apps/mariadb/Makefile @@ -4,7 +4,7 @@ include ../../../hack/common-envs.mk include ../../../hack/package.mk generate: - cozyvalues-gen -v values.yaml -s values.schema.json -r README.md + cozyvalues-gen -m 'mariadb' -v values.yaml -s values.schema.json -r README.md -g ../../../api/apps/v1alpha1/mariadb/types.go ../../../hack/update-crd.sh update: diff --git a/packages/apps/mariadb/README.md b/packages/apps/mariadb/README.md index bb3ea8ac..9d97dc5f 100644 --- a/packages/apps/mariadb/README.md +++ b/packages/apps/mariadb/README.md @@ -15,7 +15,7 @@ This managed service is controlled by mariadb-operator, ensuring efficient manag ### How to switch master/slave replica ```bash -kubectl edit mariadb +kubectl edit mariadb ``` update: @@ -54,11 +54,11 @@ more details: - **Replication can't be finished with various errors** - **Replication can't be finished in case if `binlog` purged** - Until `mariadbbackup` is not used to bootstrap a node by mariadb-operator (this feature is not inmplemented yet), follow these manual steps to fix it: + Until `mariadbbackup` is not used to bootstrap a node by mariadb-operator (this feature is not implemented yet), follow these manual steps to fix it: https://github.com/mariadb-operator/mariadb-operator/issues/141#issuecomment-1804760231 -- **Corrupted indicies** - Sometimes some indecies can be corrupted on master replica, you can recover them from slave: +- **Corrupted indices** + Sometimes some indices can be corrupted on master replica, you can recover them from slave: ```bash mysqldump -h -P 3306 -u -p --column-statistics=0 ~/tmp/fix-table.sql diff --git a/packages/apps/mariadb/images/mariadb-backup.tag b/packages/apps/mariadb/images/mariadb-backup.tag index 1e381661..6c830892 100644 --- a/packages/apps/mariadb/images/mariadb-backup.tag +++ b/packages/apps/mariadb/images/mariadb-backup.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/mariadb-backup:0.0.0@sha256:0ddbbec0568dcb9fbc317cd9cc654e826dbe88ba3f184fa9b6b58aacb93b4570 +ghcr.io/cozystack/cozystack/mariadb-backup:0.0.0@sha256:3841eb171416711977dea0cf8cd45d32344caac9727af760c37d5e1dd41ee4bb diff --git a/packages/apps/mariadb/templates/backup-cronjob.yaml b/packages/apps/mariadb/templates/backup-cronjob.yaml index 97b52208..ddb237cd 100644 --- a/packages/apps/mariadb/templates/backup-cronjob.yaml +++ b/packages/apps/mariadb/templates/backup-cronjob.yaml @@ -13,9 +13,6 @@ spec: jobTemplate: spec: backoffLimit: 2 - template: - spec: - restartPolicy: OnFailure template: metadata: annotations: @@ -44,7 +41,7 @@ spec: name: {{ .Release.Name }} key: root-password - name: MYSQL_HOST - value: {{ .Release.Name }}-secondary + value: "{{ .Release.Name }}-{{ if eq (int .Values.replicas) 1 }}primary{{ else }}secondary{{ end }}" - name: AWS_ACCESS_KEY_ID valueFrom: secretKeyRef: diff --git a/packages/apps/mariadb/templates/mariadb.yaml b/packages/apps/mariadb/templates/mariadb.yaml index 8a530134..d3d5e636 100644 --- a/packages/apps/mariadb/templates/mariadb.yaml +++ b/packages/apps/mariadb/templates/mariadb.yaml @@ -29,13 +29,11 @@ spec: - {{ .Release.Name }} topologyKey: "kubernetes.io/hostname" - {{- if gt (int .Values.replicas) 1 }} replication: enabled: true #primary: # podIndex: 0 # automaticFailover: true - {{- end }} podMetadata: labels: @@ -65,9 +63,6 @@ spec: metadata: labels: app.kubernetes.io/instance: {{ $.Release.Name }} - {{- if and .Values.external (eq (int .Values.replicas) 1) }} - type: LoadBalancer - {{- end }} storage: size: {{ .Values.size }} resizeInUseVolumes: true @@ -76,7 +71,7 @@ spec: storageClassName: {{ . }} {{- end }} - {{- if and .Values.external (gt (int .Values.replicas) 1) }} + {{- if .Values.external }} primaryService: type: LoadBalancer {{- end }} diff --git a/packages/apps/mariadb/templates/workloadmonitor.yaml b/packages/apps/mariadb/templates/workloadmonitor.yaml index b69139bf..36cb59f0 100644 --- a/packages/apps/mariadb/templates/workloadmonitor.yaml +++ b/packages/apps/mariadb/templates/workloadmonitor.yaml @@ -3,6 +3,8 @@ apiVersion: cozystack.io/v1alpha1 kind: WorkloadMonitor metadata: name: {{ $.Release.Name }} + labels: + workloads.cozystack.io/resource-preset: {{ .Values.resourcesPreset | quote }} spec: replicas: {{ .Values.replicas }} minReplicas: 1 diff --git a/packages/apps/mariadb/values.schema.json b/packages/apps/mariadb/values.schema.json index 31c56378..49df40f0 100644 --- a/packages/apps/mariadb/values.schema.json +++ b/packages/apps/mariadb/values.schema.json @@ -2,98 +2,6 @@ "title": "Chart Values", "type": "object", "properties": { - "backup": { - "description": "Backup configuration.", - "type": "object", - "default": {}, - "required": [ - "cleanupStrategy", - "enabled", - "resticPassword", - "s3AccessKey", - "s3Bucket", - "s3Region", - "s3SecretKey", - "schedule" - ], - "properties": { - "cleanupStrategy": { - "description": "Retention strategy for cleaning up old backups.", - "type": "string", - "default": "--keep-last=3 --keep-daily=3 --keep-within-weekly=1m" - }, - "enabled": { - "description": "Enable regular backups (default: false).", - "type": "boolean", - "default": false - }, - "resticPassword": { - "description": "Password for Restic backup encryption.", - "type": "string", - "default": "\u003cpassword\u003e" - }, - "s3AccessKey": { - "description": "Access key for S3 authentication.", - "type": "string", - "default": "\u003cyour-access-key\u003e" - }, - "s3Bucket": { - "description": "S3 bucket used for storing backups.", - "type": "string", - "default": "s3.example.org/mariadb-backups" - }, - "s3Region": { - "description": "AWS S3 region where backups are stored.", - "type": "string", - "default": "us-east-1" - }, - "s3SecretKey": { - "description": "Secret key for S3 authentication.", - "type": "string", - "default": "\u003cyour-secret-key\u003e" - }, - "schedule": { - "description": "Cron schedule for automated backups.", - "type": "string", - "default": "0 2 * * *" - } - } - }, - "databases": { - "description": "Databases configuration map.", - "type": "object", - "default": {}, - "additionalProperties": { - "type": "object", - "properties": { - "roles": { - "description": "Roles assigned to users.", - "type": "object", - "properties": { - "admin": { - "description": "List of users with admin privileges.", - "type": "array", - "items": { - "type": "string" - } - }, - "readonly": { - "description": "List of users with read-only privileges.", - "type": "array", - "items": { - "type": "string" - } - } - } - } - } - } - }, - "external": { - "description": "Enable external access from outside the cluster.", - "type": "boolean", - "default": false - }, "replicas": { "description": "Number of MariaDB replicas.", "type": "integer", @@ -165,6 +73,22 @@ "type": "string", "default": "" }, + "external": { + "description": "Enable external access from outside the cluster.", + "type": "boolean", + "default": false + }, + "version": { + "description": "MariaDB major.minor version to deploy", + "type": "string", + "default": "v11.8", + "enum": [ + "v11.8", + "v11.4", + "v10.11", + "v10.6" + ] + }, "users": { "description": "Users configuration map.", "type": "object", @@ -187,16 +111,92 @@ } } }, - "version": { - "description": "MariaDB major.minor version to deploy", - "type": "string", - "default": "v11.8", - "enum": [ - "v11.8", - "v11.4", - "v10.11", - "v10.6" - ] + "databases": { + "description": "Databases configuration map.", + "type": "object", + "default": {}, + "additionalProperties": { + "type": "object", + "properties": { + "roles": { + "description": "Roles assigned to users.", + "type": "object", + "properties": { + "admin": { + "description": "List of users with admin privileges.", + "type": "array", + "items": { + "type": "string" + } + }, + "readonly": { + "description": "List of users with read-only privileges.", + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + }, + "backup": { + "description": "Backup configuration.", + "type": "object", + "default": {}, + "required": [ + "cleanupStrategy", + "enabled", + "resticPassword", + "s3AccessKey", + "s3Bucket", + "s3Region", + "s3SecretKey", + "schedule" + ], + "properties": { + "cleanupStrategy": { + "description": "Retention strategy for cleaning up old backups.", + "type": "string", + "default": "--keep-last=3 --keep-daily=3 --keep-within-weekly=1m" + }, + "enabled": { + "description": "Enable regular backups (default: false).", + "type": "boolean", + "default": false + }, + "resticPassword": { + "description": "Password for Restic backup encryption.", + "type": "string", + "default": "\u003cpassword\u003e" + }, + "s3AccessKey": { + "description": "Access key for S3 authentication.", + "type": "string", + "default": "\u003cyour-access-key\u003e" + }, + "s3Bucket": { + "description": "S3 bucket used for storing backups.", + "type": "string", + "default": "s3.example.org/mariadb-backups" + }, + "s3Region": { + "description": "AWS S3 region where backups are stored.", + "type": "string", + "default": "us-east-1" + }, + "s3SecretKey": { + "description": "Secret key for S3 authentication.", + "type": "string", + "default": "\u003cyour-secret-key\u003e" + }, + "schedule": { + "description": "Cron schedule for automated backups.", + "type": "string", + "default": "0 2 * * *" + } + } } } -} \ No newline at end of file +} diff --git a/packages/apps/mongodb/Makefile b/packages/apps/mongodb/Makefile index 9440c3fd..37f30ccc 100644 --- a/packages/apps/mongodb/Makefile +++ b/packages/apps/mongodb/Makefile @@ -3,7 +3,7 @@ include ../../../hack/package.mk .PHONY: generate update generate: - cozyvalues-gen -v values.yaml -s values.schema.json -r README.md + cozyvalues-gen -m 'mongodb' -v values.yaml -s values.schema.json -r README.md -g ../../../api/apps/v1alpha1/mongodb/types.go ../../../hack/update-crd.sh update: diff --git a/packages/apps/mongodb/templates/mongodb.yaml b/packages/apps/mongodb/templates/mongodb.yaml index 67969758..9273e506 100644 --- a/packages/apps/mongodb/templates/mongodb.yaml +++ b/packages/apps/mongodb/templates/mongodb.yaml @@ -169,6 +169,8 @@ apiVersion: cozystack.io/v1alpha1 kind: WorkloadMonitor metadata: name: {{ .Release.Name }} + labels: + workloads.cozystack.io/resource-preset: {{ .Values.resourcesPreset | quote }} spec: {{- if .Values.sharding }} {{- $totalReplicas := 0 }} diff --git a/packages/apps/mongodb/values.schema.json b/packages/apps/mongodb/values.schema.json index 3a66d60d..2d077cfc 100644 --- a/packages/apps/mongodb/values.schema.json +++ b/packages/apps/mongodb/values.schema.json @@ -2,112 +2,6 @@ "title": "Chart Values", "type": "object", "properties": { - "backup": { - "description": "Backup configuration.", - "type": "object", - "default": {}, - "required": [ - "enabled" - ], - "properties": { - "destinationPath": { - "description": "Destination path for backups (e.g. s3://bucket/path/).", - "type": "string", - "default": "s3://bucket/path/to/folder/" - }, - "enabled": { - "description": "Enable regular backups.", - "type": "boolean", - "default": false - }, - "endpointURL": { - "description": "S3 endpoint URL for uploads.", - "type": "string", - "default": "http://minio-gateway-service:9000" - }, - "retentionPolicy": { - "description": "Retention policy (e.g. \"30d\").", - "type": "string", - "default": "30d" - }, - "s3AccessKey": { - "description": "Access key for S3 authentication.", - "type": "string", - "default": "" - }, - "s3SecretKey": { - "description": "Secret key for S3 authentication.", - "type": "string", - "default": "" - }, - "schedule": { - "description": "Cron schedule for automated backups.", - "type": "string", - "default": "0 2 * * *" - } - } - }, - "bootstrap": { - "description": "Bootstrap configuration.", - "type": "object", - "default": {}, - "required": [ - "backupName", - "enabled" - ], - "properties": { - "backupName": { - "description": "Name of backup to restore from.", - "type": "string", - "default": "" - }, - "enabled": { - "description": "Whether to restore from a backup.", - "type": "boolean", - "default": false - }, - "recoveryTime": { - "description": "Timestamp for point-in-time recovery; empty means latest.", - "type": "string", - "default": "" - } - } - }, - "databases": { - "description": "Databases configuration map.", - "type": "object", - "default": {}, - "additionalProperties": { - "type": "object", - "properties": { - "roles": { - "description": "Roles assigned to users.", - "type": "object", - "properties": { - "admin": { - "description": "List of users with admin privileges (readWrite + dbAdmin).", - "type": "array", - "items": { - "type": "string" - } - }, - "readonly": { - "description": "List of users with read-only privileges.", - "type": "array", - "items": { - "type": "string" - } - } - } - } - } - } - }, - "external": { - "description": "Enable external access from outside the cluster.", - "type": "boolean", - "default": false - }, "replicas": { "description": "Number of MongoDB replicas in replica set.", "type": "integer", @@ -160,6 +54,40 @@ "2xlarge" ] }, + "size": { + "description": "Persistent Volume Claim size available for application data.", + "default": "10Gi", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "storageClass": { + "description": "StorageClass used to store the data.", + "type": "string", + "default": "" + }, + "external": { + "description": "Enable external access from outside the cluster.", + "type": "boolean", + "default": false + }, + "version": { + "description": "MongoDB major version to deploy.", + "type": "string", + "default": "v8", + "enum": [ + "v8", + "v7", + "v6" + ] + }, "sharding": { "description": "Enable sharded cluster mode. When disabled, deploys a replica set.", "type": "boolean", @@ -243,25 +171,6 @@ } } }, - "size": { - "description": "Persistent Volume Claim size available for application data.", - "default": "10Gi", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - }, - "storageClass": { - "description": "StorageClass used to store the data.", - "type": "string", - "default": "" - }, "users": { "description": "Users configuration map.", "type": "object", @@ -276,15 +185,106 @@ } } }, - "version": { - "description": "MongoDB major version to deploy.", - "type": "string", - "default": "v8", - "enum": [ - "v8", - "v7", - "v6" - ] + "databases": { + "description": "Databases configuration map.", + "type": "object", + "default": {}, + "additionalProperties": { + "type": "object", + "properties": { + "roles": { + "description": "Roles assigned to users.", + "type": "object", + "properties": { + "admin": { + "description": "List of users with admin privileges (readWrite + dbAdmin).", + "type": "array", + "items": { + "type": "string" + } + }, + "readonly": { + "description": "List of users with read-only privileges.", + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + }, + "backup": { + "description": "Backup configuration.", + "type": "object", + "default": {}, + "required": [ + "enabled" + ], + "properties": { + "destinationPath": { + "description": "Destination path for backups (e.g. s3://bucket/path/).", + "type": "string", + "default": "s3://bucket/path/to/folder/" + }, + "enabled": { + "description": "Enable regular backups.", + "type": "boolean", + "default": false + }, + "endpointURL": { + "description": "S3 endpoint URL for uploads.", + "type": "string", + "default": "http://minio-gateway-service:9000" + }, + "retentionPolicy": { + "description": "Retention policy (e.g. \"30d\").", + "type": "string", + "default": "30d" + }, + "s3AccessKey": { + "description": "Access key for S3 authentication.", + "type": "string", + "default": "" + }, + "s3SecretKey": { + "description": "Secret key for S3 authentication.", + "type": "string", + "default": "" + }, + "schedule": { + "description": "Cron schedule for automated backups.", + "type": "string", + "default": "0 2 * * *" + } + } + }, + "bootstrap": { + "description": "Bootstrap configuration.", + "type": "object", + "default": {}, + "required": [ + "backupName", + "enabled" + ], + "properties": { + "backupName": { + "description": "Name of backup to restore from.", + "type": "string", + "default": "" + }, + "enabled": { + "description": "Whether to restore from a backup.", + "type": "boolean", + "default": false + }, + "recoveryTime": { + "description": "Timestamp for point-in-time recovery; empty means latest.", + "type": "string", + "default": "" + } + } } } -} \ No newline at end of file +} diff --git a/packages/apps/nats/Makefile b/packages/apps/nats/Makefile index d1cfda8e..29f6f9bc 100644 --- a/packages/apps/nats/Makefile +++ b/packages/apps/nats/Makefile @@ -1,5 +1,5 @@ include ../../../hack/package.mk generate: - cozyvalues-gen -v values.yaml -s values.schema.json -r README.md + cozyvalues-gen -m 'nats' -v values.yaml -s values.schema.json -r README.md -g ../../../api/apps/v1alpha1/nats/types.go ../../../hack/update-crd.sh diff --git a/packages/apps/nats/templates/workloadmonitor.yaml b/packages/apps/nats/templates/workloadmonitor.yaml index 43d64a46..cb20b4a6 100644 --- a/packages/apps/nats/templates/workloadmonitor.yaml +++ b/packages/apps/nats/templates/workloadmonitor.yaml @@ -3,6 +3,8 @@ apiVersion: cozystack.io/v1alpha1 kind: WorkloadMonitor metadata: name: {{ $.Release.Name }} + labels: + workloads.cozystack.io/resource-preset: {{ .Values.resourcesPreset | quote }} spec: replicas: {{ .Values.replicas }} minReplicas: 1 diff --git a/packages/apps/nats/values.schema.json b/packages/apps/nats/values.schema.json index 3ef5fef7..605c8752 100644 --- a/packages/apps/nats/values.schema.json +++ b/packages/apps/nats/values.schema.json @@ -2,60 +2,6 @@ "title": "Chart Values", "type": "object", "properties": { - "config": { - "description": "NATS configuration.", - "type": "object", - "default": {}, - "properties": { - "merge": { - "description": "Additional configuration to merge into NATS config.", - "type": "object", - "default": {}, - "x-kubernetes-preserve-unknown-fields": true - }, - "resolver": { - "description": "Additional resolver configuration to merge into NATS config.", - "type": "object", - "default": {}, - "x-kubernetes-preserve-unknown-fields": true - } - } - }, - "external": { - "description": "Enable external access from outside the cluster.", - "type": "boolean", - "default": false - }, - "jetstream": { - "description": "Jetstream configuration.", - "type": "object", - "default": {}, - "required": [ - "enabled", - "size" - ], - "properties": { - "enabled": { - "description": "Enable or disable Jetstream for persistent messaging in NATS.", - "type": "boolean", - "default": true - }, - "size": { - "description": "Jetstream persistent storage size.", - "default": "10Gi", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - } - } - }, "replicas": { "description": "Number of replicas.", "type": "integer", @@ -113,6 +59,11 @@ "type": "string", "default": "" }, + "external": { + "description": "Enable external access from outside the cluster.", + "type": "boolean", + "default": false + }, "users": { "description": "Users configuration map.", "type": "object", @@ -126,6 +77,55 @@ } } } + }, + "jetstream": { + "description": "Jetstream configuration.", + "type": "object", + "default": {}, + "required": [ + "enabled", + "size" + ], + "properties": { + "enabled": { + "description": "Enable or disable Jetstream for persistent messaging in NATS.", + "type": "boolean", + "default": true + }, + "size": { + "description": "Jetstream persistent storage size.", + "default": "10Gi", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + } + } + }, + "config": { + "description": "NATS configuration.", + "type": "object", + "default": {}, + "properties": { + "merge": { + "description": "Additional configuration to merge into NATS config.", + "type": "object", + "default": {}, + "x-kubernetes-preserve-unknown-fields": true + }, + "resolver": { + "description": "Additional resolver configuration to merge into NATS config.", + "type": "object", + "default": {}, + "x-kubernetes-preserve-unknown-fields": true + } + } } } -} \ No newline at end of file +} diff --git a/packages/apps/openbao/.helmignore b/packages/apps/openbao/.helmignore new file mode 100644 index 00000000..1ea0ae84 --- /dev/null +++ b/packages/apps/openbao/.helmignore @@ -0,0 +1,3 @@ +.helmignore +/logos +/Makefile diff --git a/packages/apps/openbao/Chart.yaml b/packages/apps/openbao/Chart.yaml new file mode 100644 index 00000000..f33fc756 --- /dev/null +++ b/packages/apps/openbao/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: openbao +description: Managed OpenBAO secrets management service +icon: /logos/openbao.svg +type: application +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process +appVersion: "2.5.0" diff --git a/packages/apps/openbao/Makefile b/packages/apps/openbao/Makefile new file mode 100644 index 00000000..b9530139 --- /dev/null +++ b/packages/apps/openbao/Makefile @@ -0,0 +1,5 @@ +include ../../../hack/package.mk + +generate: + cozyvalues-gen -m 'openbao' -v values.yaml -s values.schema.json -r README.md -g ../../../api/apps/v1alpha1/openbao/types.go + ../../../hack/update-crd.sh diff --git a/packages/apps/openbao/README.md b/packages/apps/openbao/README.md new file mode 100644 index 00000000..c53c9d28 --- /dev/null +++ b/packages/apps/openbao/README.md @@ -0,0 +1,27 @@ +# Managed OpenBAO Service + +OpenBAO is an open-source secrets management solution forked from HashiCorp Vault. +It provides identity-based secrets and encryption management for cloud infrastructure. + +## Parameters + +### Common parameters + +| Name | Description | Type | Value | +| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------- | +| `replicas` | Number of OpenBAO replicas. HA with Raft is automatically enabled when replicas > 1. Switching between standalone (file storage) and HA (Raft storage) modes requires data migration. | `int` | `1` | +| `resources` | Explicit CPU and memory configuration for each OpenBAO replica. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` | +| `resources.cpu` | CPU available to each replica. | `quantity` | `""` | +| `resources.memory` | Memory (RAM) available to each replica. | `quantity` | `""` | +| `resourcesPreset` | Default sizing preset used when `resources` is omitted. | `string` | `small` | +| `size` | Persistent Volume Claim size for data storage. | `quantity` | `10Gi` | +| `storageClass` | StorageClass used to store the data. | `string` | `""` | +| `external` | Enable external access from outside the cluster. | `bool` | `false` | + + +### Application-specific parameters + +| Name | Description | Type | Value | +| ---- | -------------------------- | ------ | ------ | +| `ui` | Enable the OpenBAO web UI. | `bool` | `true` | + diff --git a/packages/apps/openbao/charts/cozy-lib b/packages/apps/openbao/charts/cozy-lib new file mode 120000 index 00000000..e1813509 --- /dev/null +++ b/packages/apps/openbao/charts/cozy-lib @@ -0,0 +1 @@ +../../../library/cozy-lib \ No newline at end of file diff --git a/packages/apps/openbao/logos/openbao.svg b/packages/apps/openbao/logos/openbao.svg new file mode 100644 index 00000000..5ce73b79 --- /dev/null +++ b/packages/apps/openbao/logos/openbao.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/apps/openbao/templates/_resources.tpl b/packages/apps/openbao/templates/_resources.tpl new file mode 100644 index 00000000..7aeb976e --- /dev/null +++ b/packages/apps/openbao/templates/_resources.tpl @@ -0,0 +1,49 @@ +{{/* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* vim: set filetype=mustache: */}} + +{{/* +Return a resource request/limit object based on a given preset. +These presets are for basic testing and not meant to be used in production +{{ include "resources.preset" (dict "type" "nano") -}} +*/}} +{{- define "resources.preset" -}} +{{- $presets := dict + "nano" (dict + "requests" (dict "cpu" "100m" "memory" "128Mi" "ephemeral-storage" "50Mi") + "limits" (dict "memory" "128Mi" "ephemeral-storage" "2Gi") + ) + "micro" (dict + "requests" (dict "cpu" "250m" "memory" "256Mi" "ephemeral-storage" "50Mi") + "limits" (dict "memory" "256Mi" "ephemeral-storage" "2Gi") + ) + "small" (dict + "requests" (dict "cpu" "500m" "memory" "512Mi" "ephemeral-storage" "50Mi") + "limits" (dict "memory" "512Mi" "ephemeral-storage" "2Gi") + ) + "medium" (dict + "requests" (dict "cpu" "500m" "memory" "1Gi" "ephemeral-storage" "50Mi") + "limits" (dict "memory" "1Gi" "ephemeral-storage" "2Gi") + ) + "large" (dict + "requests" (dict "cpu" "1" "memory" "2Gi" "ephemeral-storage" "50Mi") + "limits" (dict "memory" "2Gi" "ephemeral-storage" "2Gi") + ) + "xlarge" (dict + "requests" (dict "cpu" "2" "memory" "4Gi" "ephemeral-storage" "50Mi") + "limits" (dict "memory" "4Gi" "ephemeral-storage" "2Gi") + ) + "2xlarge" (dict + "requests" (dict "cpu" "4" "memory" "8Gi" "ephemeral-storage" "50Mi") + "limits" (dict "memory" "8Gi" "ephemeral-storage" "2Gi") + ) + }} +{{- if hasKey $presets .type -}} +{{- index $presets .type | toYaml -}} +{{- else -}} +{{- printf "ERROR: Preset key '%s' invalid. Allowed values are %s" .type (join "," (keys $presets)) | fail -}} +{{- end -}} +{{- end -}} diff --git a/packages/apps/openbao/templates/dashboard-resourcemap.yaml b/packages/apps/openbao/templates/dashboard-resourcemap.yaml new file mode 100644 index 00000000..452a07db --- /dev/null +++ b/packages/apps/openbao/templates/dashboard-resourcemap.yaml @@ -0,0 +1,31 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ .Release.Name }}-dashboard-resources +rules: +- apiGroups: + - "" + resources: + - services + resourceNames: + - {{ .Release.Name }} + - {{ .Release.Name }}-internal + verbs: ["get", "list", "watch"] +- apiGroups: + - cozystack.io + resources: + - workloadmonitors + resourceNames: + - {{ .Release.Name }} + verbs: ["get", "list", "watch"] +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Release.Name }}-dashboard-resources +subjects: +{{ include "cozy-lib.rbac.subjectsForTenantAndAccessLevel" (list "use" .Release.Namespace) }} +roleRef: + kind: Role + name: {{ .Release.Name }}-dashboard-resources + apiGroup: rbac.authorization.k8s.io diff --git a/packages/apps/openbao/templates/openbao.yaml b/packages/apps/openbao/templates/openbao.yaml new file mode 100644 index 00000000..daa6f9c4 --- /dev/null +++ b/packages/apps/openbao/templates/openbao.yaml @@ -0,0 +1,99 @@ +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: {{ .Release.Name }}-system + labels: + sharding.fluxcd.io/key: tenants +spec: + chartRef: + kind: ExternalArtifact + name: cozystack-openbao-application-default-openbao-system + namespace: cozy-system + interval: 5m + timeout: 10m + install: + remediation: + retries: -1 + upgrade: + force: true + remediation: + retries: -1 + valuesFrom: + - kind: Secret + name: cozystack-values + values: + openbao: + fullnameOverride: {{ .Release.Name }} + global: + tlsDisable: true + server: + podManagementPolicy: Parallel + resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.resourcesPreset .Values.resources $) | nindent 10 }} + dataStorage: + enabled: true + size: {{ .Values.size }} + {{- with .Values.storageClass }} + storageClass: {{ . }} + {{- end }} + {{- if gt (int .Values.replicas) 1 }} + standalone: + enabled: false + ha: + enabled: true + replicas: {{ .Values.replicas }} + raft: + enabled: true + setNodeId: true + config: | + ui = {{ .Values.ui }} + + listener "tcp" { + address = "[::]:8200" + cluster_address = "[::]:8201" + tls_disable = true + } + + storage "raft" { + path = "/openbao/data" + {{- range $i := until (int $.Values.replicas) }} + retry_join { + leader_api_addr = "http://{{ $.Release.Name }}-{{ $i }}.{{ $.Release.Name }}-internal:8200" + } + {{- end }} + } + + service_registration "kubernetes" {} + {{- else }} + standalone: + enabled: true + config: | + ui = {{ .Values.ui }} + + listener "tcp" { + address = "[::]:8200" + cluster_address = "[::]:8201" + tls_disable = true + } + + storage "file" { + path = "/openbao/data" + } + # Note: service_registration "kubernetes" {} is intentionally omitted + # in standalone mode — it requires an HA-capable storage backend and + # causes a fatal error with storage "file". + ha: + enabled: false + {{- end }} + {{- if .Values.external }} + service: + type: LoadBalancer + {{- end }} + ui: + enabled: {{ .Values.ui }} + {{- if .Values.external }} + serviceType: LoadBalancer + {{- end }} + injector: + enabled: false + csi: + enabled: false diff --git a/packages/apps/openbao/templates/workloadmonitor.yaml b/packages/apps/openbao/templates/workloadmonitor.yaml new file mode 100644 index 00000000..56f9ec8e --- /dev/null +++ b/packages/apps/openbao/templates/workloadmonitor.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: WorkloadMonitor +metadata: + name: {{ $.Release.Name }} + labels: + workloads.cozystack.io/resource-preset: {{ .Values.resourcesPreset | quote }} +spec: + replicas: {{ .Values.replicas }} + minReplicas: 1 + kind: openbao + type: openbao + selector: + app.kubernetes.io/instance: {{ $.Release.Name }}-system + version: {{ $.Chart.Version }} diff --git a/packages/apps/openbao/values.schema.json b/packages/apps/openbao/values.schema.json new file mode 100644 index 00000000..9b32ddd6 --- /dev/null +++ b/packages/apps/openbao/values.schema.json @@ -0,0 +1,87 @@ +{ + "title": "Chart Values", + "type": "object", + "properties": { + "replicas": { + "description": "Number of OpenBAO replicas. HA with Raft is automatically enabled when replicas \u003e 1. Switching between standalone (file storage) and HA (Raft storage) modes requires data migration.", + "type": "integer", + "default": 1 + }, + "resources": { + "description": "Explicit CPU and memory configuration for each OpenBAO replica. When omitted, the preset defined in `resourcesPreset` is applied.", + "type": "object", + "default": {}, + "properties": { + "cpu": { + "description": "CPU available to each replica.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "memory": { + "description": "Memory (RAM) available to each replica.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + } + } + }, + "resourcesPreset": { + "description": "Default sizing preset used when `resources` is omitted.", + "type": "string", + "default": "small", + "enum": [ + "nano", + "micro", + "small", + "medium", + "large", + "xlarge", + "2xlarge" + ] + }, + "size": { + "description": "Persistent Volume Claim size for data storage.", + "default": "10Gi", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "storageClass": { + "description": "StorageClass used to store the data.", + "type": "string", + "default": "" + }, + "external": { + "description": "Enable external access from outside the cluster.", + "type": "boolean", + "default": false + }, + "ui": { + "description": "Enable the OpenBAO web UI.", + "type": "boolean", + "default": true + } + } +} diff --git a/packages/apps/openbao/values.yaml b/packages/apps/openbao/values.yaml new file mode 100644 index 00000000..fc8b75cc --- /dev/null +++ b/packages/apps/openbao/values.yaml @@ -0,0 +1,41 @@ +## +## @section Common parameters +## + +## @typedef {struct} Resources - Explicit CPU and memory configuration for each OpenBAO replica. +## @field {quantity} [cpu] - CPU available to each replica. +## @field {quantity} [memory] - Memory (RAM) available to each replica. + +## @enum {string} ResourcesPreset - Default sizing preset. +## @value nano +## @value micro +## @value small +## @value medium +## @value large +## @value xlarge +## @value 2xlarge + +## @param {int} replicas - Number of OpenBAO replicas. HA with Raft is automatically enabled when replicas > 1. Switching between standalone (file storage) and HA (Raft storage) modes requires data migration. +replicas: 1 + +## @param {Resources} [resources] - Explicit CPU and memory configuration for each OpenBAO replica. When omitted, the preset defined in `resourcesPreset` is applied. +resources: {} + +## @param {ResourcesPreset} resourcesPreset="small" - Default sizing preset used when `resources` is omitted. +resourcesPreset: "small" + +## @param {quantity} size - Persistent Volume Claim size for data storage. +size: 10Gi + +## @param {string} storageClass - StorageClass used to store the data. +storageClass: "" + +## @param {bool} external - Enable external access from outside the cluster. +external: false + +## +## @section Application-specific parameters +## + +## @param {bool} ui - Enable the OpenBAO web UI. +ui: true diff --git a/packages/apps/opensearch/.helmignore b/packages/apps/opensearch/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/packages/apps/opensearch/.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/packages/apps/opensearch/Chart.yaml b/packages/apps/opensearch/Chart.yaml new file mode 100644 index 00000000..8abefadf --- /dev/null +++ b/packages/apps/opensearch/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: opensearch +description: Managed OpenSearch service +icon: /logos/opensearch.svg +type: application +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process +appVersion: "2.11.1" diff --git a/packages/apps/opensearch/Makefile b/packages/apps/opensearch/Makefile new file mode 100644 index 00000000..1781661f --- /dev/null +++ b/packages/apps/opensearch/Makefile @@ -0,0 +1,11 @@ +include ../../../hack/package.mk + +.PHONY: generate update + +generate: + cozyvalues-gen -m 'opensearch' -v values.yaml -s values.schema.json -r README.md -g ../../../api/apps/v1alpha1/opensearch/types.go + ../../../hack/update-crd.sh + +update: + hack/update-versions.sh + $(MAKE) generate diff --git a/packages/apps/opensearch/README.md b/packages/apps/opensearch/README.md new file mode 100644 index 00000000..d968c665 --- /dev/null +++ b/packages/apps/opensearch/README.md @@ -0,0 +1,60 @@ +# Managed OpenSearch Service + +## Parameters + +### Common parameters + +| Name | Description | Type | Value | +| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------- | +| `replicas` | Number of OpenSearch nodes in the cluster. | `int` | `3` | +| `resources` | Explicit CPU and memory configuration for each OpenSearch node. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` | +| `resources.cpu` | CPU available to each node. | `quantity` | `""` | +| `resources.memory` | Memory (RAM) available to each node. | `quantity` | `""` | +| `resourcesPreset` | Default sizing preset used when `resources` is omitted. OpenSearch requires minimum 2Gi memory. | `string` | `large` | +| `size` | Persistent Volume Claim size available for application data. | `quantity` | `10Gi` | +| `storageClass` | StorageClass used to store the data. | `string` | `""` | +| `external` | Enable external access from outside the cluster. | `bool` | `false` | +| `topologySpreadPolicy` | How strictly to enforce pod distribution across nodes and zones. | `string` | `soft` | +| `version` | OpenSearch major version to deploy. | `string` | `v2` | + + +### Image configuration + +| Name | Description | Type | Value | +| ------------------- | -------------------------------------- | -------- | ----- | +| `images` | Container images used by the operator. | `object` | `{}` | +| `images.opensearch` | OpenSearch image. | `string` | `""` | + + +### Node roles configuration + +| Name | Description | Type | Value | +| ------------------ | ----------------------------- | -------- | ------- | +| `nodeRoles` | Node roles configuration. | `object` | `{}` | +| `nodeRoles.master` | Enable cluster_manager role. | `bool` | `true` | +| `nodeRoles.data` | Enable data role. | `bool` | `true` | +| `nodeRoles.ingest` | Enable ingest role. | `bool` | `true` | +| `nodeRoles.ml` | Enable machine learning role. | `bool` | `false` | + + +### Users configuration + +| Name | Description | Type | Value | +| ---------------------- | -------------------------------------------------- | ------------------- | ----- | +| `users` | Custom OpenSearch users configuration map. | `map[string]object` | `{}` | +| `users[name].password` | Password for the user (auto-generated if omitted). | `string` | `""` | +| `users[name].roles` | List of OpenSearch roles. | `[]string` | `[]` | + + +### OpenSearch Dashboards configuration + +| Name | Description | Type | Value | +| ----------------------------- | ----------------------------------------------------- | ---------- | -------- | +| `dashboards` | OpenSearch Dashboards configuration. | `object` | `{}` | +| `dashboards.enabled` | Enable OpenSearch Dashboards deployment. | `bool` | `false` | +| `dashboards.replicas` | Number of Dashboards replicas. | `int` | `1` | +| `dashboards.resources` | Explicit CPU and memory configuration for Dashboards. | `object` | `{}` | +| `dashboards.resources.cpu` | CPU available to each node. | `quantity` | `""` | +| `dashboards.resources.memory` | Memory (RAM) available to each node. | `quantity` | `""` | +| `dashboards.resourcesPreset` | Default sizing preset for Dashboards. | `string` | `medium` | + diff --git a/packages/apps/opensearch/charts/cozy-lib b/packages/apps/opensearch/charts/cozy-lib new file mode 120000 index 00000000..e1813509 --- /dev/null +++ b/packages/apps/opensearch/charts/cozy-lib @@ -0,0 +1 @@ +../../../library/cozy-lib \ No newline at end of file diff --git a/packages/apps/opensearch/files/versions.yaml b/packages/apps/opensearch/files/versions.yaml new file mode 100644 index 00000000..be39fc80 --- /dev/null +++ b/packages/apps/opensearch/files/versions.yaml @@ -0,0 +1,5 @@ +# OpenSearch version mapping (major version -> image tag) +# Auto-generated by hack/update-versions.sh - do not edit manually +v3: "3.0.0" +v2: "2.11.1" +v1: "1.3.20" diff --git a/packages/apps/opensearch/hack/update-versions.sh b/packages/apps/opensearch/hack/update-versions.sh new file mode 100755 index 00000000..b2c4c946 --- /dev/null +++ b/packages/apps/opensearch/hack/update-versions.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +OPENSEARCH_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +VERSIONS_FILE="${OPENSEARCH_DIR}/files/versions.yaml" + +# Supported major versions (newest first) +SUPPORTED_MAJOR_VERSIONS="3 2 1" + +echo "Supported major versions: $SUPPORTED_MAJOR_VERSIONS" + +# Check if skopeo is installed +if ! command -v skopeo &> /dev/null; then + echo "Error: skopeo is not installed. Please install skopeo and try again." >&2 + exit 1 +fi + +# Check if jq is installed +if ! command -v jq &> /dev/null; then + echo "Error: jq is not installed. Please install jq and try again." >&2 + exit 1 +fi + +# Get available image tags from Docker Hub +IMAGE="docker.io/opensearchproject/opensearch" +echo "Fetching available image tags from registry..." +TAGS=$(skopeo list-tags "docker://${IMAGE}" | jq -r '.Tags[]') + +echo "# OpenSearch version mapping (major version -> image tag)" > "${VERSIONS_FILE}" +echo "# Auto-generated by hack/update-versions.sh - do not edit manually" >> "${VERSIONS_FILE}" + +for MAJOR in $SUPPORTED_MAJOR_VERSIONS; do + # Find the latest stable release for this major version + LATEST=$(echo "$TAGS" \ + | grep -E "^${MAJOR}\.[0-9]+\.[0-9]+$" \ + | sort -t. -k1,1n -k2,2n -k3,3n \ + | tail -1) + + if [ -n "$LATEST" ]; then + echo "v${MAJOR}: latest tag is ${LATEST}" + echo "v${MAJOR}: \"${LATEST}\"" >> "${VERSIONS_FILE}" + else + echo "WARNING: No stable release found for major version ${MAJOR}" >&2 + fi +done + +echo "" +echo "Updated ${VERSIONS_FILE}:" +cat "${VERSIONS_FILE}" diff --git a/packages/apps/opensearch/logos/opensearch.svg b/packages/apps/opensearch/logos/opensearch.svg new file mode 100644 index 00000000..345fdd0a --- /dev/null +++ b/packages/apps/opensearch/logos/opensearch.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/apps/opensearch/templates/.gitkeep b/packages/apps/opensearch/templates/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packages/apps/opensearch/templates/_versions.tpl b/packages/apps/opensearch/templates/_versions.tpl new file mode 100644 index 00000000..4eae4163 --- /dev/null +++ b/packages/apps/opensearch/templates/_versions.tpl @@ -0,0 +1,13 @@ +{{/* +Version mapping helper +Loads version mapping from files/versions.yaml and returns the full version for a given major version +*/}} +{{- define "opensearch.versionMap" -}} +{{- $versions := .Files.Get "files/versions.yaml" | fromYaml -}} +{{- $version := .Values.version | default "v2" -}} +{{- if hasKey $versions $version -}} +{{- index $versions $version -}} +{{- else -}} +{{- fail (printf "Invalid version '%s'. Available versions: %s" $version (keys $versions | join ", ")) -}} +{{- end -}} +{{- end -}} diff --git a/packages/apps/opensearch/templates/dashboard-resourcemap.yaml b/packages/apps/opensearch/templates/dashboard-resourcemap.yaml new file mode 100644 index 00000000..91dbf8cb --- /dev/null +++ b/packages/apps/opensearch/templates/dashboard-resourcemap.yaml @@ -0,0 +1,42 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ .Release.Name }}-dashboard-resources +rules: +- apiGroups: + - "" + resources: + - services + resourceNames: + - {{ .Release.Name }} + - {{ .Release.Name }}-external + {{- if .Values.dashboards.enabled }} + - {{ .Release.Name }}-dashboards + - {{ .Release.Name }}-dashboards-external + {{- end }} + verbs: ["get", "list", "watch"] +- apiGroups: + - "" + resources: + - secrets + resourceNames: + - {{ .Release.Name }}-credentials + verbs: ["get", "list", "watch"] +- apiGroups: + - cozystack.io + resources: + - workloadmonitors + resourceNames: + - {{ .Release.Name }} + verbs: ["get", "list", "watch"] +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Release.Name }}-dashboard-resources +subjects: +{{ include "cozy-lib.rbac.subjectsForTenantAndAccessLevel" (list "use" .Release.Namespace) }} +roleRef: + kind: Role + name: {{ .Release.Name }}-dashboard-resources + apiGroup: rbac.authorization.k8s.io diff --git a/packages/apps/opensearch/templates/external-svc.yaml b/packages/apps/opensearch/templates/external-svc.yaml new file mode 100644 index 00000000..f28626ab --- /dev/null +++ b/packages/apps/opensearch/templates/external-svc.yaml @@ -0,0 +1,39 @@ +{{- if .Values.external }} +{{- $clusterDomain := (index .Values._cluster "cluster-domain") | default "cozy.local" }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-external + annotations: + external-dns.alpha.kubernetes.io/hostname: {{ .Release.Name }}.{{ .Release.Namespace }}.{{ $clusterDomain }} +spec: + type: LoadBalancer + selector: + opster.io/opensearch-cluster: {{ .Release.Name }} + opster.io/opensearch-nodepool: nodes + ports: + - name: https + port: 9200 + targetPort: 9200 + protocol: TCP +{{- if .Values.dashboards.enabled }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-dashboards-external + annotations: + external-dns.alpha.kubernetes.io/hostname: {{ .Release.Name }}-dashboards.{{ .Release.Namespace }}.{{ $clusterDomain }} +spec: + type: LoadBalancer + selector: + opster.io/opensearch-cluster: {{ .Release.Name }} + app.kubernetes.io/component: dashboards + ports: + - name: https + port: 5601 + targetPort: 5601 + protocol: TCP +{{- end }} +{{- end }} diff --git a/packages/apps/opensearch/templates/opensearch.yaml b/packages/apps/opensearch/templates/opensearch.yaml new file mode 100644 index 00000000..836a3d4b --- /dev/null +++ b/packages/apps/opensearch/templates/opensearch.yaml @@ -0,0 +1,105 @@ +{{- $topologyMode := .Values.topologySpreadPolicy | default "soft" }} +{{- $whenUnsatisfiable := "ScheduleAnyway" }} +{{- if eq $topologyMode "hard" }} +{{- $whenUnsatisfiable = "DoNotSchedule" }} +{{- end }} +--- +apiVersion: opensearch.opster.io/v1 +kind: OpenSearchCluster +metadata: + name: {{ .Release.Name }} +spec: + general: + serviceName: {{ .Release.Name }} + version: {{ include "opensearch.versionMap" $ }} + httpPort: 9200 + setVMMaxMapCount: true + drainDataNodes: true + {{- if gt (len .Values.images.opensearch) 0 }} + image: {{ .Values.images.opensearch }} + {{- end }} + bootstrap: + resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.resourcesPreset .Values.resources $) | nindent 6 }} + security: + tls: + transport: + generate: true + perNode: true + http: + generate: true + config: + securityConfigSecret: + name: {{ .Release.Name }}-security-config + adminCredentialsSecret: + name: {{ .Release.Name }}-admin-credentials + {{- if .Values.dashboards.enabled }} + dashboards: + enable: true + version: {{ include "opensearch.versionMap" $ }} + replicas: {{ .Values.dashboards.replicas | default 1 }} + tls: + enable: true + generate: true + resources: {{- include "cozy-lib.resources.defaultingSanitize" (list (.Values.dashboards.resourcesPreset | default "medium") .Values.dashboards.resources $) | nindent 6 }} + {{- end }} + nodePools: + - component: nodes + replicas: {{ .Values.replicas }} + diskSize: {{ .Values.size }} + persistence: + pvc: + accessModes: + - ReadWriteOnce + {{- if .Values.storageClass }} + storageClass: {{ .Values.storageClass }} + {{- else if eq (int .Values.replicas) 1 }} + storageClass: replicated + {{- else }} + storageClass: local + {{- end }} + resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.resourcesPreset .Values.resources $) | nindent 8 }} + {{- if not (or .Values.nodeRoles.master .Values.nodeRoles.data .Values.nodeRoles.ingest .Values.nodeRoles.ml) }} + {{- fail "At least one node role must be enabled (master, data, ingest, or ml)" }} + {{- end }} + roles: + {{- if .Values.nodeRoles.master }} + - cluster_manager + {{- end }} + {{- if .Values.nodeRoles.data }} + - data + {{- end }} + {{- if .Values.nodeRoles.ingest }} + - ingest + {{- end }} + {{- if .Values.nodeRoles.ml }} + - ml + {{- end }} + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: {{ $whenUnsatisfiable }} + labelSelector: + matchLabels: + opster.io/opensearch-cluster: {{ .Release.Name }} + - maxSkew: 1 + topologyKey: topology.kubernetes.io/zone + whenUnsatisfiable: {{ $whenUnsatisfiable }} + labelSelector: + matchLabels: + opster.io/opensearch-cluster: {{ .Release.Name }} +--- +# WorkloadMonitor tracks OpenSearch pods +apiVersion: cozystack.io/v1alpha1 +kind: WorkloadMonitor +metadata: + name: {{ .Release.Name }} + labels: + workloads.cozystack.io/resource-preset: {{ .Values.resourcesPreset | quote }} +spec: + replicas: {{ .Values.replicas }} + minReplicas: 1 + kind: opensearch + type: opensearch + selector: + opster.io/opensearch-cluster: {{ .Release.Name }} + version: {{ .Chart.Version }} diff --git a/packages/apps/opensearch/templates/security.yaml b/packages/apps/opensearch/templates/security.yaml new file mode 100644 index 00000000..d10d353a --- /dev/null +++ b/packages/apps/opensearch/templates/security.yaml @@ -0,0 +1,120 @@ +{{- $clusterDomain := (index .Values._cluster "cluster-domain") | default "cozy.local" }} +{{- $existingAdminCreds := lookup "v1" "Secret" .Release.Namespace (printf "%s-admin-credentials" .Release.Name) }} +{{- $existingSecurityConfig := lookup "v1" "Secret" .Release.Namespace (printf "%s-security-config" .Release.Name) }} +{{- $password := randAlphaNum 32 }} +{{- if and $existingAdminCreds (hasKey $existingAdminCreds.data "password") }} +{{- $password = index $existingAdminCreds.data "password" | b64dec }} +{{- end }} +--- +# Admin credentials for the OpenSearch operator (referenced by adminCredentialsSecret) +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Release.Name }}-admin-credentials + labels: + app.kubernetes.io/name: opensearch + app.kubernetes.io/instance: {{ .Release.Name }} +type: Opaque +stringData: + username: admin + password: {{ $password | quote }} +--- +# Security plugin configuration (referenced by securityConfigSecret) +# On upgrades, the existing secret is preserved to avoid bcrypt hash regeneration +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Release.Name }}-security-config + labels: + app.kubernetes.io/name: opensearch + app.kubernetes.io/instance: {{ .Release.Name }} +type: Opaque +{{- if and $existingSecurityConfig (hasKey $existingSecurityConfig.data "internal_users.yml") }} +data: + {{- range $key, $val := $existingSecurityConfig.data }} + {{ $key }}: {{ $val }} + {{- end }} +{{- else }} +stringData: + config.yml: | + --- + _meta: + type: "config" + config_version: 2 + config: + dynamic: + http: + anonymous_auth_enabled: false + authc: + basic_internal_auth_domain: + description: "Authenticate via HTTP Basic against internal users database" + http_enabled: true + transport_enabled: true + order: 0 + http_authenticator: + type: basic + challenge: true + authentication_backend: + type: internal + + internal_users.yml: | + --- + _meta: + type: "internalusers" + config_version: 2 + admin: + hash: {{ htpasswd "admin" $password | trimPrefix "admin:" | quote }} + reserved: true + backend_roles: + - "admin" + description: "Admin user" + + roles.yml: | + --- + _meta: + type: "roles" + config_version: 2 + + roles_mapping.yml: | + --- + _meta: + type: "rolesmapping" + config_version: 2 + all_access: + reserved: false + backend_roles: + - "admin" + + action_groups.yml: | + --- + _meta: + type: "actiongroups" + config_version: 2 + + tenants.yml: | + --- + _meta: + type: "tenants" + config_version: 2 +{{- end }} +--- +# User-facing credentials with connection URI +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Release.Name }}-credentials + labels: + app.kubernetes.io/name: opensearch + app.kubernetes.io/instance: {{ .Release.Name }} +type: Opaque +stringData: + username: admin + password: {{ $password | quote }} + host: {{ .Release.Name }}.{{ .Release.Namespace }}.svc.{{ $clusterDomain }} + port: "9200" + uri: https://admin:{{ $password | urlquery }}@{{ .Release.Name }}.{{ .Release.Namespace }}.svc.{{ $clusterDomain }}:9200 + {{- if .Values.dashboards.enabled }} + dashboards-host: {{ .Release.Name }}-dashboards.{{ .Release.Namespace }}.svc.{{ $clusterDomain }} + dashboards-port: "5601" + dashboards-uri: https://admin:{{ $password | urlquery }}@{{ .Release.Name }}-dashboards.{{ .Release.Namespace }}.svc.{{ $clusterDomain }}:5601 + {{- end }} diff --git a/packages/apps/opensearch/templates/users.yaml b/packages/apps/opensearch/templates/users.yaml new file mode 100644 index 00000000..1d326206 --- /dev/null +++ b/packages/apps/opensearch/templates/users.yaml @@ -0,0 +1,21 @@ +{{- range $username, $user := .Values.users }} +{{- $existingSecret := lookup "v1" "Secret" $.Release.Namespace (printf "%s-user-%s" $.Release.Name $username) }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ $.Release.Name }}-user-{{ $username }} + labels: + opensearch.opster.io/credentials: "true" +type: kubernetes.io/basic-auth +stringData: + username: {{ $username | quote }} + {{- if $user.password }} + password: {{ $user.password | quote }} + {{- else if and $existingSecret (hasKey $existingSecret.data "password") }} + password: {{ index $existingSecret.data "password" | b64dec | quote }} + {{- else }} + password: {{ randAlphaNum 16 | quote }} + {{- end }} + roles: {{ $user.roles | default (list "readall") | join "," | quote }} +{{- end }} diff --git a/packages/apps/opensearch/tests/opensearch_test.yaml b/packages/apps/opensearch/tests/opensearch_test.yaml new file mode 100644 index 00000000..2959ec18 --- /dev/null +++ b/packages/apps/opensearch/tests/opensearch_test.yaml @@ -0,0 +1,532 @@ +suite: opensearch CR tests + +templates: + - templates/opensearch.yaml + +tests: + ################### + # Basic rendering # + ################### + + - it: renders OpenSearchCluster and WorkloadMonitor + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - hasDocuments: + count: 2 + - isKind: + of: OpenSearchCluster + documentIndex: 0 + - isKind: + of: WorkloadMonitor + documentIndex: 1 + + - it: sets correct CR name + release: + name: my-opensearch + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: metadata.name + value: my-opensearch + documentIndex: 0 + + ################## + # Version # + ################## + + - it: defaults to version 2.11.1 + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: spec.general.version + value: "2.11.1" + documentIndex: 0 + + - it: sets version 3.0.0 when v3 selected + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + version: v3 + asserts: + - equal: + path: spec.general.version + value: "3.0.0" + documentIndex: 0 + + - it: sets version 1.3.20 when v1 selected + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + version: v1 + asserts: + - equal: + path: spec.general.version + value: "1.3.20" + documentIndex: 0 + + ##################### + # General # + ##################### + + - it: sets drainDataNodes to true + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: spec.general.drainDataNodes + value: true + documentIndex: 0 + + - it: sets httpPort to 9200 + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: spec.general.httpPort + value: 9200 + documentIndex: 0 + + ##################### + # Security # + ##################### + + - it: always includes security section with TLS + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: spec.security.tls.transport.generate + value: true + documentIndex: 0 + - equal: + path: spec.security.tls.transport.perNode + value: true + documentIndex: 0 + - equal: + path: spec.security.tls.http.generate + value: true + documentIndex: 0 + + - it: references security config secret + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: spec.security.config.securityConfigSecret.name + value: test-os-security-config + documentIndex: 0 + + - it: references admin credentials secret + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: spec.security.config.adminCredentialsSecret.name + value: test-os-admin-credentials + documentIndex: 0 + + - it: does not disable security plugin + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - notExists: + path: spec.general.additionalConfig + documentIndex: 0 + + ##################### + # Node Pools # + ##################### + + - it: sets default replica count to 3 + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: spec.nodePools[0].replicas + value: 3 + documentIndex: 0 + + - it: sets replica count from values + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + replicas: 5 + asserts: + - equal: + path: spec.nodePools[0].replicas + value: 5 + documentIndex: 0 + + ##################### + # Node Roles # + ##################### + + - it: enables default node roles + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - contains: + path: spec.nodePools[0].roles + content: cluster_manager + documentIndex: 0 + - contains: + path: spec.nodePools[0].roles + content: data + documentIndex: 0 + - contains: + path: spec.nodePools[0].roles + content: ingest + documentIndex: 0 + + - it: disables master role when configured + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + nodeRoles: + master: false + data: true + ingest: true + ml: false + asserts: + - notContains: + path: spec.nodePools[0].roles + content: cluster_manager + documentIndex: 0 + + - it: fails when all node roles are disabled + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + nodeRoles: + master: false + data: false + ingest: false + ml: false + asserts: + - failedTemplate: + errorMessage: "At least one node role must be enabled (master, data, ingest, or ml)" + + - it: enables ml role when configured + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + nodeRoles: + master: true + data: true + ingest: true + ml: true + asserts: + - contains: + path: spec.nodePools[0].roles + content: ml + documentIndex: 0 + + ########################### + # Topology Spread Policy # + ########################### + + - it: uses soft topology spread by default (ScheduleAnyway) + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: spec.nodePools[0].topologySpreadConstraints[0].whenUnsatisfiable + value: ScheduleAnyway + documentIndex: 0 + - equal: + path: spec.nodePools[0].topologySpreadConstraints[1].whenUnsatisfiable + value: ScheduleAnyway + documentIndex: 0 + + - it: uses hard topology spread when configured (DoNotSchedule) + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + topologySpreadPolicy: hard + asserts: + - equal: + path: spec.nodePools[0].topologySpreadConstraints[0].whenUnsatisfiable + value: DoNotSchedule + documentIndex: 0 + - equal: + path: spec.nodePools[0].topologySpreadConstraints[1].whenUnsatisfiable + value: DoNotSchedule + documentIndex: 0 + + - it: sets correct topology keys + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: spec.nodePools[0].topologySpreadConstraints[0].topologyKey + value: kubernetes.io/hostname + documentIndex: 0 + - equal: + path: spec.nodePools[0].topologySpreadConstraints[1].topologyKey + value: topology.kubernetes.io/zone + documentIndex: 0 + + ##################### + # Storage # + ##################### + + - it: sets accessModes to ReadWriteOnce + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - contains: + path: spec.nodePools[0].persistence.pvc.accessModes + content: ReadWriteOnce + documentIndex: 0 + + - it: sets storage size from values + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + size: 50Gi + asserts: + - equal: + path: spec.nodePools[0].diskSize + value: 50Gi + documentIndex: 0 + + - it: uses local storageClass when replicas > 1 and no storageClass + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + storageClass: "" + replicas: 3 + asserts: + - equal: + path: spec.nodePools[0].persistence.pvc.storageClass + value: local + documentIndex: 0 + + - it: uses replicated storageClass when single replica and no storageClass + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + storageClass: "" + replicas: 1 + asserts: + - equal: + path: spec.nodePools[0].persistence.pvc.storageClass + value: replicated + documentIndex: 0 + + - it: uses custom storageClass when provided + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + storageClass: fast-ssd + asserts: + - equal: + path: spec.nodePools[0].persistence.pvc.storageClass + value: fast-ssd + documentIndex: 0 + + ##################### + # Dashboards # + ##################### + + - it: does not render dashboards when disabled + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + dashboards: + enabled: false + replicas: 1 + resourcesPreset: "medium" + resources: {} + asserts: + - notExists: + path: spec.dashboards + documentIndex: 0 + + - it: renders dashboards when enabled + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + dashboards: + enabled: true + replicas: 1 + resourcesPreset: "medium" + resources: {} + asserts: + - equal: + path: spec.dashboards.enable + value: true + documentIndex: 0 + - equal: + path: spec.dashboards.replicas + value: 1 + documentIndex: 0 + - equal: + path: spec.dashboards.tls.enable + value: true + documentIndex: 0 + - equal: + path: spec.dashboards.tls.generate + value: true + documentIndex: 0 + + ########################### + # WorkloadMonitor # + ########################### + + - it: creates WorkloadMonitor with correct metadata + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: metadata.name + value: test-os + documentIndex: 1 + - equal: + path: spec.kind + value: opensearch + documentIndex: 1 + - equal: + path: spec.type + value: opensearch + documentIndex: 1 + + - it: sets replicas in WorkloadMonitor + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + replicas: 5 + asserts: + - equal: + path: spec.replicas + value: 5 + documentIndex: 1 + + - it: sets minReplicas to 1 + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: spec.minReplicas + value: 1 + documentIndex: 1 + + - it: sets correct selector labels + release: + name: mydb + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: spec.selector["opster.io/opensearch-cluster"] + value: mydb + documentIndex: 1 diff --git a/packages/apps/opensearch/tests/security_test.yaml b/packages/apps/opensearch/tests/security_test.yaml new file mode 100644 index 00000000..cd8740d9 --- /dev/null +++ b/packages/apps/opensearch/tests/security_test.yaml @@ -0,0 +1,207 @@ +suite: security secrets tests + +templates: + - templates/security.yaml + +tests: + ################### + # Basic rendering # + ################### + + - it: renders three secrets (admin-credentials, security-config, credentials) + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - hasDocuments: + count: 3 + + ########################### + # Admin credentials # + ########################### + + - it: sets admin-credentials secret name + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: metadata.name + value: test-os-admin-credentials + documentIndex: 0 + + - it: sets admin username + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: stringData.username + value: admin + documentIndex: 0 + + - it: generates a 32-char alphanumeric password + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - matchRegex: + path: stringData.password + pattern: "^[a-zA-Z0-9]{32}$" + documentIndex: 0 + + ########################### + # Security config # + ########################### + + - it: sets security-config secret name + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: metadata.name + value: test-os-security-config + documentIndex: 1 + + - it: includes config.yml with basic auth + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - exists: + path: stringData["config.yml"] + documentIndex: 1 + + - it: includes internal_users.yml with bcrypt hash + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - matchRegex: + path: stringData["internal_users.yml"] + pattern: "hash:.*\\$2a\\$" + documentIndex: 1 + + - it: includes roles_mapping.yml + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - exists: + path: stringData["roles_mapping.yml"] + documentIndex: 1 + + ########################### + # User-facing credentials # + ########################### + + - it: sets credentials secret name + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: metadata.name + value: test-os-credentials + documentIndex: 2 + + - it: sets correct host and port + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: stringData.host + value: test-os.tenant-test.svc.cozy.local + documentIndex: 2 + - equal: + path: stringData.port + value: "9200" + documentIndex: 2 + + - it: sets https URI with credentials + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - matchRegex: + path: stringData.uri + pattern: "^https://admin:[a-zA-Z0-9]{32}@test-os\\.tenant-test\\.svc\\.cozy\\.local:9200$" + documentIndex: 2 + + - it: does not include dashboards fields when dashboards disabled + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + dashboards: + enabled: false + replicas: 1 + resourcesPreset: "medium" + resources: {} + asserts: + - notExists: + path: stringData.dashboards-host + documentIndex: 2 + + - it: includes dashboards fields when dashboards enabled + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + dashboards: + enabled: true + replicas: 1 + resourcesPreset: "medium" + resources: {} + asserts: + - equal: + path: stringData.dashboards-host + value: test-os-dashboards.tenant-test.svc.cozy.local + documentIndex: 2 + - equal: + path: stringData.dashboards-port + value: "5601" + documentIndex: 2 + - matchRegex: + path: stringData.dashboards-uri + pattern: "^https://admin:[a-zA-Z0-9]{32}@test-os-dashboards\\.tenant-test\\.svc\\.cozy\\.local:5601$" + documentIndex: 2 diff --git a/packages/apps/opensearch/tests/users_test.yaml b/packages/apps/opensearch/tests/users_test.yaml new file mode 100644 index 00000000..f6b4990e --- /dev/null +++ b/packages/apps/opensearch/tests/users_test.yaml @@ -0,0 +1,176 @@ +suite: users secrets tests + +templates: + - templates/users.yaml + +tests: + ################### + # Basic rendering # + ################### + + - it: does not render secrets when no users defined + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + users: {} + asserts: + - hasDocuments: + count: 0 + + - it: renders one secret per user + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + users: + user1: + roles: + - readall + user2: + roles: + - all_access + asserts: + - hasDocuments: + count: 2 + + ##################### + # Secret metadata # + ##################### + + - it: sets correct secret name + release: + name: my-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + users: + myuser: + roles: + - readall + asserts: + - equal: + path: metadata.name + value: my-os-user-myuser + + - it: sets opensearch credentials label + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + users: + myuser: + roles: + - readall + asserts: + - equal: + path: metadata.labels["opensearch.opster.io/credentials"] + value: "true" + + - it: uses basic-auth secret type + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + users: + myuser: + roles: + - readall + asserts: + - equal: + path: type + value: kubernetes.io/basic-auth + + ##################### + # Secret data # + ##################### + + - it: sets username in secret + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + users: + testuser: + roles: + - readall + asserts: + - equal: + path: stringData.username + value: testuser + + - it: uses provided password + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + users: + myuser: + password: mysecretpassword + roles: + - readall + asserts: + - equal: + path: stringData.password + value: mysecretpassword + + - it: generates password when not provided + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + users: + myuser: + roles: + - readall + asserts: + - matchRegex: + path: stringData.password + pattern: "^[a-zA-Z0-9]{16}$" + + - it: sets roles as comma-separated string + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + users: + myuser: + roles: + - readall + - reporting_user + - monitoring_user + asserts: + - equal: + path: stringData.roles + value: "readall,reporting_user,monitoring_user" + + - it: defaults to readall role when no roles specified + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + users: + myuser: {} + asserts: + - equal: + path: stringData.roles + value: "readall" diff --git a/packages/apps/opensearch/values.schema.json b/packages/apps/opensearch/values.schema.json new file mode 100644 index 00000000..04b530ba --- /dev/null +++ b/packages/apps/opensearch/values.schema.json @@ -0,0 +1,239 @@ +{ + "title": "Chart Values", + "type": "object", + "properties": { + "replicas": { + "description": "Number of OpenSearch nodes in the cluster.", + "type": "integer", + "default": 3 + }, + "resources": { + "description": "Explicit CPU and memory configuration for each OpenSearch node. When omitted, the preset defined in `resourcesPreset` is applied.", + "type": "object", + "default": {}, + "properties": { + "cpu": { + "description": "CPU available to each node.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "memory": { + "description": "Memory (RAM) available to each node.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + } + } + }, + "resourcesPreset": { + "description": "Default sizing preset used when `resources` is omitted. OpenSearch requires minimum 2Gi memory.", + "type": "string", + "default": "large", + "enum": [ + "nano", + "micro", + "small", + "medium", + "large", + "xlarge", + "2xlarge" + ] + }, + "size": { + "description": "Persistent Volume Claim size available for application data.", + "default": "10Gi", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "storageClass": { + "description": "StorageClass used to store the data.", + "type": "string", + "default": "" + }, + "external": { + "description": "Enable external access from outside the cluster.", + "type": "boolean", + "default": false + }, + "topologySpreadPolicy": { + "description": "How strictly to enforce pod distribution across nodes and zones.", + "type": "string", + "default": "soft", + "enum": [ + "soft", + "hard" + ] + }, + "version": { + "description": "OpenSearch major version to deploy.", + "type": "string", + "default": "v2", + "enum": [ + "v3", + "v2", + "v1" + ] + }, + "images": { + "description": "Container images used by the operator.", + "type": "object", + "default": {}, + "required": [ + "opensearch" + ], + "properties": { + "opensearch": { + "description": "OpenSearch image.", + "type": "string", + "default": "" + } + } + }, + "nodeRoles": { + "description": "Node roles configuration.", + "type": "object", + "default": {}, + "required": [ + "data", + "ingest", + "master", + "ml" + ], + "properties": { + "data": { + "description": "Enable data role.", + "type": "boolean", + "default": true + }, + "ingest": { + "description": "Enable ingest role.", + "type": "boolean", + "default": true + }, + "master": { + "description": "Enable cluster_manager role.", + "type": "boolean", + "default": true + }, + "ml": { + "description": "Enable machine learning role.", + "type": "boolean", + "default": false + } + } + }, + "users": { + "description": "Custom OpenSearch users configuration map.", + "type": "object", + "default": {}, + "additionalProperties": { + "type": "object", + "properties": { + "password": { + "description": "Password for the user (auto-generated if omitted).", + "type": "string" + }, + "roles": { + "description": "List of OpenSearch roles.", + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "dashboards": { + "description": "OpenSearch Dashboards configuration.", + "type": "object", + "default": {}, + "required": [ + "enabled", + "replicas", + "resourcesPreset" + ], + "properties": { + "enabled": { + "description": "Enable OpenSearch Dashboards deployment.", + "type": "boolean", + "default": false + }, + "replicas": { + "description": "Number of Dashboards replicas.", + "type": "integer", + "default": 1 + }, + "resources": { + "description": "Explicit CPU and memory configuration for Dashboards.", + "type": "object", + "default": {}, + "properties": { + "cpu": { + "description": "CPU available to each node.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "memory": { + "description": "Memory (RAM) available to each node.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + } + } + }, + "resourcesPreset": { + "description": "Default sizing preset for Dashboards.", + "type": "string", + "default": "medium", + "enum": [ + "nano", + "micro", + "small", + "medium", + "large", + "xlarge", + "2xlarge" + ] + } + } + } + } +} diff --git a/packages/apps/opensearch/values.yaml b/packages/apps/opensearch/values.yaml new file mode 100644 index 00000000..8de805e3 --- /dev/null +++ b/packages/apps/opensearch/values.yaml @@ -0,0 +1,111 @@ +## +## @section Common parameters +## + +## @typedef {struct} Resources - Explicit CPU and memory configuration for each OpenSearch node. +## @field {quantity} [cpu] - CPU available to each node. +## @field {quantity} [memory] - Memory (RAM) available to each node. + +## @enum {string} ResourcesPreset - Default sizing preset. +## @value nano +## @value micro +## @value small +## @value medium +## @value large +## @value xlarge +## @value 2xlarge + +## @param {int} replicas - Number of OpenSearch nodes in the cluster. +replicas: 3 + +## @param {Resources} [resources] - Explicit CPU and memory configuration for each OpenSearch node. When omitted, the preset defined in `resourcesPreset` is applied. +resources: {} + +## @param {ResourcesPreset} resourcesPreset="large" - Default sizing preset used when `resources` is omitted. OpenSearch requires minimum 2Gi memory. +resourcesPreset: "large" + +## @param {quantity} size - Persistent Volume Claim size available for application data. +size: 10Gi + +## @param {string} storageClass - StorageClass used to store the data. +storageClass: "" + +## @param {bool} external - Enable external access from outside the cluster. +external: false + +## @enum {string} TopologySpreadPolicy - Pod distribution policy across nodes/zones. +## @value soft - Best-effort distribution (ScheduleAnyway) - pods may be scheduled on same node if needed +## @value hard - Strict distribution (DoNotSchedule) - pods will not schedule if spread cannot be achieved + +## @param {TopologySpreadPolicy} topologySpreadPolicy="soft" - How strictly to enforce pod distribution across nodes and zones. +topologySpreadPolicy: "soft" + +## +## @enum {string} Version +## @value v3 +## @value v2 +## @value v1 + +## @param {Version} version - OpenSearch major version to deploy. +version: v2 + +## +## @section Image configuration +## + +## @typedef {struct} Images - Container image configuration. +## @field {string} opensearch - OpenSearch image. + +## @param {Images} images - Container images used by the operator. +images: + opensearch: "" + +## +## @section Node roles configuration +## + +## @typedef {struct} NodeRoles - OpenSearch node roles. +## @field {bool} master - Enable cluster_manager role. +## @field {bool} data - Enable data role. +## @field {bool} ingest - Enable ingest role. +## @field {bool} ml - Enable machine learning role. + +## @param {NodeRoles} nodeRoles - Node roles configuration. +nodeRoles: + master: true + data: true + ingest: true + ml: false + +## +## @section Users configuration +## + +## @typedef {struct} User - User configuration. +## @field {string} [password] - Password for the user (auto-generated if omitted). +## @field {[]string} roles - List of OpenSearch roles. + +## @param {map[string]User} users - Custom OpenSearch users configuration map. +users: {} +## Example: +## users: +## myuser: +## roles: +## - all_access + +## +## @section OpenSearch Dashboards configuration +## + +## @typedef {struct} Dashboards - OpenSearch Dashboards deployment configuration. +## @field {bool} enabled - Enable OpenSearch Dashboards deployment. +## @field {int} replicas - Number of Dashboards replicas. +## @field {Resources} [resources] - Explicit CPU and memory configuration for Dashboards. +## @field {ResourcesPreset} resourcesPreset - Default sizing preset for Dashboards. + +## @param {Dashboards} dashboards - OpenSearch Dashboards configuration. +dashboards: + enabled: false + replicas: 1 + resources: {} + resourcesPreset: "medium" diff --git a/packages/apps/postgres/Makefile b/packages/apps/postgres/Makefile index aa9b0ccc..dff0b9c6 100644 --- a/packages/apps/postgres/Makefile +++ b/packages/apps/postgres/Makefile @@ -1,7 +1,7 @@ include ../../../hack/package.mk generate: - cozyvalues-gen -v values.yaml -s values.schema.json -r README.md + cozyvalues-gen -m 'postgresql' -v values.yaml -s values.schema.json -r README.md -g ../../../api/apps/v1alpha1/postgresql/types.go ../../../hack/update-crd.sh update: diff --git a/packages/apps/postgres/README.md b/packages/apps/postgres/README.md index 5a550f7a..4cda7284 100644 --- a/packages/apps/postgres/README.md +++ b/packages/apps/postgres/README.md @@ -133,12 +133,13 @@ See: ### Bootstrap (recovery) parameters -| Name | Description | Type | Value | -| ------------------------ | ------------------------------------------------------------------- | -------- | ------- | -| `bootstrap` | Bootstrap configuration. | `object` | `{}` | -| `bootstrap.enabled` | Whether to restore from a backup. | `bool` | `false` | -| `bootstrap.recoveryTime` | Timestamp (RFC3339) for point-in-time recovery; empty means latest. | `string` | `""` | -| `bootstrap.oldName` | Previous cluster name before deletion. | `string` | `""` | +| Name | Description | Type | Value | +| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------- | +| `bootstrap` | Bootstrap configuration. | `object` | `{}` | +| `bootstrap.enabled` | Whether to restore from a backup. | `bool` | `false` | +| `bootstrap.recoveryTime` | Timestamp (RFC3339) for point-in-time recovery; empty means latest. | `string` | `""` | +| `bootstrap.oldName` | Previous cluster name before deletion. | `string` | `""` | +| `bootstrap.serverName` | Barman server name (S3 path prefix) used by the original cluster when writing backups. Set this only when the original cluster had an explicit barmanObjectStore.serverName that differed from its Kubernetes resource name. | `string` | `""` | ## Parameter examples and reference diff --git a/packages/apps/postgres/templates/db.yaml b/packages/apps/postgres/templates/db.yaml index 7557c436..2cdaec1d 100644 --- a/packages/apps/postgres/templates/db.yaml +++ b/packages/apps/postgres/templates/db.yaml @@ -32,6 +32,9 @@ spec: - name: {{ .Values.bootstrap.oldName }} barmanObjectStore: destinationPath: {{ .Values.backup.destinationPath }} + {{- if .Values.bootstrap.serverName }} + serverName: {{ .Values.bootstrap.serverName }} + {{- end }} endpointURL: {{ .Values.backup.endpointURL }} s3Credentials: accessKeyId: @@ -84,6 +87,8 @@ apiVersion: cozystack.io/v1alpha1 kind: WorkloadMonitor metadata: name: {{ $.Release.Name }} + labels: + workloads.cozystack.io/resource-preset: {{ .Values.resourcesPreset | quote }} spec: replicas: {{ .Values.replicas }} minReplicas: 1 diff --git a/packages/apps/postgres/templates/init-script.yaml b/packages/apps/postgres/templates/init-script.yaml index 80f7c4c7..500d54d4 100644 --- a/packages/apps/postgres/templates/init-script.yaml +++ b/packages/apps/postgres/templates/init-script.yaml @@ -66,6 +66,38 @@ stringData: EOT done + echo "== delete databases" + MANAGED_DBS=$(psql -v ON_ERROR_STOP=1 -t -A -c "SELECT datname FROM pg_database d JOIN pg_shdescription s ON d.oid = s.objoid WHERE s.description = 'database managed by helm'") + DEFINED_DBS="{{ join " " (keys .Values.databases) }}" + DELETE_DBS=$(for db in $MANAGED_DBS; do case " $DEFINED_DBS " in *" $db "*) :;; *) echo $db;; esac; done) + + echo "databases to delete: $DELETE_DBS" + for db in $DELETE_DBS; do + psql -v ON_ERROR_STOP=1 --echo-all -c "DROP DATABASE IF EXISTS \"$db\" WITH (FORCE);" + done + + echo "== delete orphaned managed roles" + MANAGED_ROLES=$(psql -v ON_ERROR_STOP=1 -t -A -c "SELECT rolname FROM pg_roles r JOIN pg_shdescription s ON r.oid = s.objoid WHERE s.description = 'role managed by helm'") + DEFINED_ROLES="{{ range $database, $d := .Values.databases }}{{ $database }}_admin {{ $database }}_readonly {{ end }}" + DELETE_ROLES=$(for role in $MANAGED_ROLES; do case " $DEFINED_ROLES " in *" $role "*) :;; *) echo $role;; esac; done) + + echo "roles to delete: $DELETE_ROLES" + for role in $DELETE_ROLES; do + psql -v ON_ERROR_STOP=1 --echo-all < /dev/null; then + echo "Error: jq is not installed. Please install jq and try again." >&2 + exit 1 +fi + +# Fetch releases from GitHub API +echo "Fetching releases from GitHub API..." +RELEASES_JSON=$(curl -sSL "${GITHUB_API_URL}?per_page=100") + +if [ -z "$RELEASES_JSON" ]; then + echo "Error: Could not fetch releases from GitHub API" >&2 + exit 1 +fi + +# Extract stable release tags (format: v3.13.7, v4.0.3, etc.) +# Filter out pre-releases and draft releases +RELEASE_TAGS=$(echo "$RELEASES_JSON" | jq -r '.[] | select(.prerelease == false) | select(.draft == false) | .tag_name' | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V) + +if [ -z "$RELEASE_TAGS" ]; then + echo "Error: Could not find any stable release tags" >&2 + exit 1 +fi + +echo "Found release tags: $(echo "$RELEASE_TAGS" | tr '\n' ' ')" + +# Supported major.minor versions (newest first) +# We support the last few minor releases of each active major +SUPPORTED_MAJORS=("4.2" "4.1" "4.0" "3.13") + +# Build versions map: major.minor -> latest patch version +declare -A VERSION_MAP +MAJOR_VERSIONS=() + +for major_minor in "${SUPPORTED_MAJORS[@]}"; do + # Find the latest patch version for this major.minor + MATCHING=$(echo "$RELEASE_TAGS" | grep -E "^v${major_minor//./\\.}\.[0-9]+$" | tail -n1) + + if [ -n "$MATCHING" ]; then + # Strip the 'v' prefix for the value (Docker tag format is e.g. 3.13.7) + TAG_VERSION="${MATCHING#v}" + VERSION_MAP["v${major_minor}"]="${TAG_VERSION}" + MAJOR_VERSIONS+=("v${major_minor}") + echo "Found version: v${major_minor} -> ${TAG_VERSION}" + else + echo "Warning: No stable releases found for ${major_minor}, skipping..." >&2 + fi +done + +if [ ${#MAJOR_VERSIONS[@]} -eq 0 ]; then + echo "Error: No matching versions found" >&2 + exit 1 +fi + +echo "Major versions to add: ${MAJOR_VERSIONS[*]}" + +# Create/update versions.yaml file +echo "Updating $VERSIONS_FILE..." +{ + for major_ver in "${MAJOR_VERSIONS[@]}"; do + echo "\"${major_ver}\": \"${VERSION_MAP[$major_ver]}\"" + done +} > "$VERSIONS_FILE" + +echo "Successfully updated $VERSIONS_FILE" + +# Update values.yaml - enum with major.minor versions only +TEMP_FILE=$(mktemp) +trap "rm -f $TEMP_FILE" EXIT + +# Build new version section +NEW_VERSION_SECTION="## @enum {string} Version" +for major_ver in "${MAJOR_VERSIONS[@]}"; do + NEW_VERSION_SECTION="${NEW_VERSION_SECTION} +## @value $major_ver" +done +NEW_VERSION_SECTION="${NEW_VERSION_SECTION} + +## @param {Version} version - RabbitMQ major.minor version to deploy +version: ${MAJOR_VERSIONS[0]}" + +# Check if version section already exists +if grep -q "^## @enum {string} Version" "$VALUES_FILE"; then + # Version section exists, update it using awk + echo "Updating existing version section in $VALUES_FILE..." + + awk -v new_section="$NEW_VERSION_SECTION" ' + /^## @enum {string} Version/ { + in_section = 1 + print new_section + next + } + in_section && /^version: / { + in_section = 0 + next + } + in_section { + next + } + { print } + ' "$VALUES_FILE" > "$TEMP_FILE.tmp" + mv "$TEMP_FILE.tmp" "$VALUES_FILE" +else + # Version section doesn't exist, insert it before Application-specific parameters section + echo "Inserting new version section in $VALUES_FILE..." + + awk -v new_section="$NEW_VERSION_SECTION" ' + /^## @section Application-specific parameters/ { + print new_section + print "" + } + { print } + ' "$VALUES_FILE" > "$TEMP_FILE.tmp" + mv "$TEMP_FILE.tmp" "$VALUES_FILE" +fi + +echo "Successfully updated $VALUES_FILE with major.minor versions: ${MAJOR_VERSIONS[*]}" diff --git a/packages/apps/rabbitmq/templates/_versions.tpl b/packages/apps/rabbitmq/templates/_versions.tpl new file mode 100644 index 00000000..76955b33 --- /dev/null +++ b/packages/apps/rabbitmq/templates/_versions.tpl @@ -0,0 +1,8 @@ +{{- define "rabbitmq.versionMap" }} +{{- $versionMap := .Files.Get "files/versions.yaml" | fromYaml }} +{{- if not (hasKey $versionMap .Values.version) }} + {{- printf `RabbitMQ version %s is not supported, allowed versions are %s` $.Values.version (keys $versionMap) | fail }} +{{- end }} +{{- index $versionMap .Values.version }} +{{- end }} + diff --git a/packages/apps/rabbitmq/templates/rabbitmq.yaml b/packages/apps/rabbitmq/templates/rabbitmq.yaml index b111285e..bbf2efbe 100644 --- a/packages/apps/rabbitmq/templates/rabbitmq.yaml +++ b/packages/apps/rabbitmq/templates/rabbitmq.yaml @@ -7,6 +7,7 @@ metadata: app.kubernetes.io/managed-by: {{ .Release.Service }} spec: replicas: {{ .Values.replicas }} + image: 'rabbitmq:{{ include "rabbitmq.versionMap" $ }}-management' {{- if .Values.external }} service: type: LoadBalancer diff --git a/packages/apps/rabbitmq/templates/workloadmonitor.yaml b/packages/apps/rabbitmq/templates/workloadmonitor.yaml index 0f7462c7..66941153 100644 --- a/packages/apps/rabbitmq/templates/workloadmonitor.yaml +++ b/packages/apps/rabbitmq/templates/workloadmonitor.yaml @@ -3,6 +3,8 @@ apiVersion: cozystack.io/v1alpha1 kind: WorkloadMonitor metadata: name: {{ $.Release.Name }} + labels: + workloads.cozystack.io/resource-preset: {{ .Values.resourcesPreset | quote }} spec: replicas: {{ .Values.replicas }} minReplicas: 1 diff --git a/packages/apps/rabbitmq/values.schema.json b/packages/apps/rabbitmq/values.schema.json index 04b8fa11..43eaa206 100644 --- a/packages/apps/rabbitmq/values.schema.json +++ b/packages/apps/rabbitmq/values.schema.json @@ -2,11 +2,6 @@ "title": "Chart Values", "type": "object", "properties": { - "external": { - "description": "Enable external access from outside the cluster.", - "type": "boolean", - "default": false - }, "replicas": { "description": "Number of RabbitMQ replicas.", "type": "integer", @@ -78,6 +73,22 @@ "type": "string", "default": "" }, + "external": { + "description": "Enable external access from outside the cluster.", + "type": "boolean", + "default": false + }, + "version": { + "description": "RabbitMQ major.minor version to deploy", + "type": "string", + "default": "v4.2", + "enum": [ + "v4.2", + "v4.1", + "v4.0", + "v3.13" + ] + }, "users": { "description": "Users configuration map.", "type": "object", @@ -126,4 +137,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/apps/rabbitmq/values.yaml b/packages/apps/rabbitmq/values.yaml index 65d5de44..08b6f5e6 100644 --- a/packages/apps/rabbitmq/values.yaml +++ b/packages/apps/rabbitmq/values.yaml @@ -34,6 +34,15 @@ storageClass: "" external: false ## +## @enum {string} Version +## @value v4.2 +## @value v4.1 +## @value v4.0 +## @value v3.13 + +## @param {Version} version - RabbitMQ major.minor version to deploy +version: v4.2 + ## @section Application-specific parameters ## diff --git a/packages/apps/redis/Makefile b/packages/apps/redis/Makefile index aa9b0ccc..939b75ee 100644 --- a/packages/apps/redis/Makefile +++ b/packages/apps/redis/Makefile @@ -1,7 +1,7 @@ include ../../../hack/package.mk generate: - cozyvalues-gen -v values.yaml -s values.schema.json -r README.md + cozyvalues-gen -m 'redis' -v values.yaml -s values.schema.json -r README.md -g ../../../api/apps/v1alpha1/redis/types.go ../../../hack/update-crd.sh update: diff --git a/packages/apps/redis/templates/redisfailover.yaml b/packages/apps/redis/templates/redisfailover.yaml index 936217a3..160e030d 100644 --- a/packages/apps/redis/templates/redisfailover.yaml +++ b/packages/apps/redis/templates/redisfailover.yaml @@ -75,6 +75,8 @@ kind: WorkloadMonitor metadata: name: {{ $.Release.Name }}-redis namespace: {{ $.Release.Namespace }} + labels: + workloads.cozystack.io/resource-preset: {{ .Values.resourcesPreset | quote }} spec: minReplicas: 1 replicas: {{ .Values.replicas }} @@ -90,6 +92,8 @@ kind: WorkloadMonitor metadata: name: {{ $.Release.Name }}-sentinel namespace: {{ $.Release.Namespace }} + labels: + workloads.cozystack.io/resource-preset: {{ .Values.resourcesPreset | quote }} spec: minReplicas: 2 replicas: 3 diff --git a/packages/apps/redis/values.schema.json b/packages/apps/redis/values.schema.json index 5c01cc31..a93d92ac 100644 --- a/packages/apps/redis/values.schema.json +++ b/packages/apps/redis/values.schema.json @@ -2,16 +2,6 @@ "title": "Chart Values", "type": "object", "properties": { - "authEnabled": { - "description": "Enable password generation.", - "type": "boolean", - "default": true - }, - "external": { - "description": "Enable external access from outside the cluster.", - "type": "boolean", - "default": false - }, "replicas": { "description": "Number of Redis replicas.", "type": "integer", @@ -83,6 +73,11 @@ "type": "string", "default": "" }, + "external": { + "description": "Enable external access from outside the cluster.", + "type": "boolean", + "default": false + }, "version": { "description": "Redis major version to deploy", "type": "string", @@ -91,6 +86,11 @@ "v8", "v7" ] + }, + "authEnabled": { + "description": "Enable password generation.", + "type": "boolean", + "default": true } } -} \ No newline at end of file +} diff --git a/packages/apps/tcp-balancer/Makefile b/packages/apps/tcp-balancer/Makefile index d1cfda8e..e3bf78fe 100644 --- a/packages/apps/tcp-balancer/Makefile +++ b/packages/apps/tcp-balancer/Makefile @@ -1,5 +1,5 @@ include ../../../hack/package.mk generate: - cozyvalues-gen -v values.yaml -s values.schema.json -r README.md + cozyvalues-gen -m 'tcpbalancer' -v values.yaml -s values.schema.json -r README.md -g ../../../api/apps/v1alpha1/tcpbalancer/types.go ../../../hack/update-crd.sh diff --git a/packages/apps/tcp-balancer/templates/workloadmonitor.yaml b/packages/apps/tcp-balancer/templates/workloadmonitor.yaml index 41dce2ab..3478826b 100644 --- a/packages/apps/tcp-balancer/templates/workloadmonitor.yaml +++ b/packages/apps/tcp-balancer/templates/workloadmonitor.yaml @@ -3,6 +3,8 @@ apiVersion: cozystack.io/v1alpha1 kind: WorkloadMonitor metadata: name: {{ $.Release.Name }} + labels: + workloads.cozystack.io/resource-preset: {{ .Values.resourcesPreset | quote }} spec: replicas: {{ .Values.replicas }} minReplicas: 1 diff --git a/packages/apps/tcp-balancer/values.schema.json b/packages/apps/tcp-balancer/values.schema.json index ea30664a..20d04d80 100644 --- a/packages/apps/tcp-balancer/values.schema.json +++ b/packages/apps/tcp-balancer/values.schema.json @@ -2,6 +2,58 @@ "title": "Chart Values", "type": "object", "properties": { + "replicas": { + "description": "Number of HAProxy replicas.", + "type": "integer", + "default": 2 + }, + "resources": { + "description": "Explicit CPU and memory configuration for each TCP Balancer replica. When omitted, the preset defined in `resourcesPreset` is applied.", + "type": "object", + "default": {}, + "properties": { + "cpu": { + "description": "CPU available to each replica.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "memory": { + "description": "Memory (RAM) available to each replica.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + } + } + }, + "resourcesPreset": { + "description": "Default sizing preset used when `resources` is omitted.", + "type": "string", + "default": "nano", + "enum": [ + "nano", + "micro", + "small", + "medium", + "large", + "xlarge", + "2xlarge" + ] + }, "external": { "description": "Enable external access from outside the cluster.", "type": "boolean", @@ -56,57 +108,10 @@ } } }, - "replicas": { - "description": "Number of HAProxy replicas.", - "type": "integer", - "default": 2 - }, - "resources": { - "description": "Explicit CPU and memory configuration for each TCP Balancer replica. When omitted, the preset defined in `resourcesPreset` is applied.", - "type": "object", - "default": {}, - "properties": { - "cpu": { - "description": "CPU available to each replica.", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - }, - "memory": { - "description": "Memory (RAM) available to each replica.", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - } - } - }, - "resourcesPreset": { - "description": "Default sizing preset used when `resources` is omitted.", - "type": "string", - "default": "nano", - "enum": [ - "nano", - "micro", - "small", - "medium", - "large", - "xlarge", - "2xlarge" - ] + "whitelistHTTP": { + "description": "Secure HTTP by whitelisting client networks (default: false).", + "type": "boolean", + "default": false }, "whitelist": { "description": "List of allowed client networks.", @@ -115,11 +120,6 @@ "items": { "type": "string" } - }, - "whitelistHTTP": { - "description": "Secure HTTP by whitelisting client networks (default: false).", - "type": "boolean", - "default": false } } -} \ No newline at end of file +} diff --git a/packages/apps/tenant/Makefile b/packages/apps/tenant/Makefile index d1cfda8e..2f51d836 100644 --- a/packages/apps/tenant/Makefile +++ b/packages/apps/tenant/Makefile @@ -1,5 +1,8 @@ include ../../../hack/package.mk generate: - cozyvalues-gen -v values.yaml -s values.schema.json -r README.md + cozyvalues-gen -m 'tenant' -v values.yaml -s values.schema.json -r README.md -g ../../../api/apps/v1alpha1/tenant/types.go ../../../hack/update-crd.sh + +test: + helm unittest . diff --git a/packages/apps/tenant/README.md b/packages/apps/tenant/README.md index 472ccce2..96cf5c6e 100644 --- a/packages/apps/tenant/README.md +++ b/packages/apps/tenant/README.md @@ -6,15 +6,15 @@ Tenants can be created recursively and are subject to the following rules: ### Tenant naming -Tenant names must follow DNS-1035 naming rules: -- Must start with a lowercase letter (`a-z`) -- Can only contain lowercase letters, numbers, and hyphens (`a-z`, `0-9`, `-`) -- Must end with a letter or number (not a hyphen) +Tenant names must be alphanumeric: + +- Lowercase letters (`a-z`) and digits (`0-9`) only +- Must start with a lowercase letter +- Dashes (`-`) are **not allowed**, unlike with other services - Maximum length depends on the cluster configuration (Helm release prefix and root domain) -**Note:** Using dashes (`-`) in tenant names is **allowed but discouraged**, unlike with other services. -This is to keep consistent naming in tenants, nested tenants, and services deployed in them. -Names with dashes (e.g., `foo-bar`) may lead to ambiguous parsing of internal resource names like `tenant-foo-bar`. +This restriction exists to keep consistent naming in tenants, nested tenants, and services deployed in them. +A tenant cannot be named `foo-bar` because parsing internal resource names like `tenant-foo-bar` would be ambiguous. For example: @@ -74,14 +74,15 @@ tenant-u1 ### Common parameters -| Name | Description | Type | Value | -| ---------------- | -------------------------------------------------------------------------------------------------------------------------- | --------------------- | ------- | -| `host` | The hostname used to access tenant services (defaults to using the tenant name as a subdomain for its parent tenant host). | `string` | `""` | -| `etcd` | Deploy own Etcd cluster. | `bool` | `false` | -| `monitoring` | Deploy own Monitoring Stack. | `bool` | `false` | -| `ingress` | Deploy own Ingress Controller. | `bool` | `false` | -| `seaweedfs` | Deploy own SeaweedFS. | `bool` | `false` | -| `resourceQuotas` | Define resource quotas for the tenant. | `map[string]quantity` | `{}` | +| Name | Description | Type | Value | +| ----------------- | -------------------------------------------------------------------------------------------------------------------------- | --------------------- | ------- | +| `host` | The hostname used to access tenant services (defaults to using the tenant name as a subdomain for its parent tenant host). | `string` | `""` | +| `etcd` | Deploy own Etcd cluster. | `bool` | `false` | +| `monitoring` | Deploy own Monitoring Stack. | `bool` | `false` | +| `ingress` | Deploy own Ingress Controller. | `bool` | `false` | +| `seaweedfs` | Deploy own SeaweedFS. | `bool` | `false` | +| `schedulingClass` | The name of a SchedulingClass CR to apply scheduling constraints for this tenant's workloads. | `string` | `""` | +| `resourceQuotas` | Define resource quotas for the tenant. | `map[string]quantity` | `{}` | ## Configuration diff --git a/packages/apps/tenant/templates/cilium-lb-pool.yaml b/packages/apps/tenant/templates/cilium-lb-pool.yaml new file mode 100644 index 00000000..63dbcaca --- /dev/null +++ b/packages/apps/tenant/templates/cilium-lb-pool.yaml @@ -0,0 +1,28 @@ +{{- $exposeMode := (index .Values._cluster "expose-mode") | default "externalIPs" }} +{{- $exposeIngress := (index .Values._cluster "expose-ingress") | default "tenant-root" }} +{{- $exposeIPs := (index .Values._cluster "expose-external-ips") | default "" | nospace }} +{{- $ipsList := list }} +{{- range splitList "," $exposeIPs }} + {{- $ip := . | trim }} + {{- if $ip }} + {{- $ipsList = append $ipsList $ip }} + {{- end }} +{{- end }} +{{- $isPublishingIngressLB := and + (eq $exposeMode "loadBalancer") + (eq $exposeIngress .Release.Namespace) + .Values.ingress }} +{{- if and $isPublishingIngressLB $ipsList }} +apiVersion: cilium.io/v2 +kind: CiliumLoadBalancerIPPool +metadata: + name: {{ trimPrefix "tenant-" .Release.Namespace }}-exposure +spec: + blocks: + {{- range $ipsList }} + - cidr: {{ . }}{{ if not (contains "/" .) }}/{{ if contains ":" . }}128{{ else }}32{{ end }}{{ end }} + {{- end }} + serviceSelector: + matchLabels: + "io.kubernetes.service.namespace": {{ .Release.Namespace | quote }} +{{- end }} diff --git a/packages/apps/tenant/templates/etcd.yaml b/packages/apps/tenant/templates/etcd.yaml index 97b39205..9a122da2 100644 --- a/packages/apps/tenant/templates/etcd.yaml +++ b/packages/apps/tenant/templates/etcd.yaml @@ -18,7 +18,7 @@ spec: name: cozystack-etcd-application-default-etcd namespace: cozy-system interval: 5m - timeout: 10m + timeout: 30m install: remediation: retries: -1 diff --git a/packages/apps/tenant/templates/namespace.yaml b/packages/apps/tenant/templates/namespace.yaml index 5942cba3..dfb83730 100644 --- a/packages/apps/tenant/templates/namespace.yaml +++ b/packages/apps/tenant/templates/namespace.yaml @@ -38,6 +38,12 @@ {{- if .Values.seaweedfs }} {{- $seaweedfs = $tenantName }} {{- end }} + +{{/* SchedulingClass: inherited from parent, can be set if parent has none */}} +{{- $schedulingClass := $parentNamespace.schedulingClass | default "" }} +{{- if and (not $schedulingClass) .Values.schedulingClass }} +{{- $schedulingClass = .Values.schedulingClass }} +{{- end }} --- apiVersion: v1 kind: Namespace @@ -58,6 +64,9 @@ metadata: namespace.cozystack.io/monitoring: {{ $monitoring | quote }} namespace.cozystack.io/seaweedfs: {{ $seaweedfs | quote }} namespace.cozystack.io/host: {{ $computedHost | quote }} + {{- with $schedulingClass }} + scheduler.cozystack.io/scheduling-class: {{ . | quote }} + {{- end }} alpha.kubevirt.io/auto-memory-limits-ratio: "1.0" ownerReferences: - apiVersion: v1 @@ -86,4 +95,7 @@ stringData: monitoring: {{ $monitoring | quote }} seaweedfs: {{ $seaweedfs | quote }} host: {{ $computedHost | quote }} + {{- with $schedulingClass }} + schedulingClass: {{ . | quote }} + {{- end }} {{- end }} diff --git a/packages/apps/tenant/templates/networkpolicy.yaml b/packages/apps/tenant/templates/networkpolicy.yaml index d9f87856..4bd3ba02 100644 --- a/packages/apps/tenant/templates/networkpolicy.yaml +++ b/packages/apps/tenant/templates/networkpolicy.yaml @@ -186,6 +186,23 @@ spec: --- apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy +metadata: + # Allow the tenant's ingress-nginx to reach both the linstor-gui + # gatekeeper pods (for /oauth2/* and app traffic) AND the transient + # ACME http-01 solver pods that cert-manager spins up in the same + # namespace during certificate issuance/renewal. Matching the whole + # namespace mirrors allow-to-dashboard / allow-to-keycloak. + name: allow-to-linstor-gui + namespace: {{ include "tenant.name" . }} +spec: + endpointSelector: {} + egress: + - toEndpoints: + - matchLabels: + "k8s:io.kubernetes.pod.namespace": cozy-linstor +--- +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy metadata: name: allow-to-keycloak namespace: {{ include "tenant.name" . }} @@ -207,6 +224,27 @@ spec: - toEndpoints: - matchLabels: "k8s:io.kubernetes.pod.namespace": cozy-kubevirt-cdi +{{- if .Values.monitoring }} +--- +apiVersion: cilium.io/v2 +kind: CiliumClusterwideNetworkPolicy +metadata: + name: {{ include "tenant.name" . }}-egress-virt-handler +spec: + endpointSelector: + matchLabels: + "k8s:io.kubernetes.pod.namespace": "{{ include "tenant.name" . }}" + "k8s:app.kubernetes.io/name": "vmagent" + egress: + - toEndpoints: + - matchLabels: + "k8s:kubevirt.io": "virt-handler" + "k8s:io.kubernetes.pod.namespace": "cozy-kubevirt" + toPorts: + - ports: + - port: "8443" + protocol: TCP +{{- end }} --- apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy diff --git a/packages/apps/tenant/tests/exposure_test.yaml b/packages/apps/tenant/tests/exposure_test.yaml new file mode 100644 index 00000000..e86fab5b --- /dev/null +++ b/packages/apps/tenant/tests/exposure_test.yaml @@ -0,0 +1,141 @@ +suite: tenant CiliumLoadBalancerIPPool rendering for publishing.exposure=loadBalancer +templates: + - templates/cilium-lb-pool.yaml + +release: + name: tenant-root + namespace: tenant-root + +tests: + - it: default exposure (externalIPs) renders no pool + set: + ingress: true + _cluster: + expose-ingress: tenant-root + expose-external-ips: "192.0.2.10,192.0.2.11" + asserts: + - hasDocuments: + count: 0 + + - it: loadBalancer mode in publishing tenant with ingress=true renders v2 pool with namespace-only selector + set: + ingress: true + _cluster: + expose-ingress: tenant-root + expose-external-ips: "192.0.2.10,192.0.2.11" + expose-mode: loadBalancer + asserts: + - hasDocuments: + count: 1 + - equal: + path: apiVersion + value: cilium.io/v2 + - equal: + path: kind + value: CiliumLoadBalancerIPPool + - equal: + path: metadata.name + value: root-exposure + - equal: + path: spec.blocks + value: + - cidr: 192.0.2.10/32 + - cidr: 192.0.2.11/32 + - equal: + path: spec.serviceSelector.matchLabels["io.kubernetes.service.namespace"] + value: tenant-root + - notExists: + path: spec.serviceSelector.matchLabels["app.kubernetes.io/name"] + + - it: loadBalancer mode with IPv6 emits /128 CIDR + set: + ingress: true + _cluster: + expose-ingress: tenant-root + expose-external-ips: "2001:db8::1" + expose-mode: loadBalancer + asserts: + - equal: + path: spec.blocks + value: + - cidr: 2001:db8::1/128 + + - it: loadBalancer mode with mixed IPv4 and IPv6 emits correct CIDR per family + set: + ingress: true + _cluster: + expose-ingress: tenant-root + expose-external-ips: "192.0.2.10,2001:db8::1" + expose-mode: loadBalancer + asserts: + - equal: + path: spec.blocks + value: + - cidr: 192.0.2.10/32 + - cidr: 2001:db8::1/128 + + - it: loadBalancer mode accepts pre-CIDR input without double-suffixing + set: + ingress: true + _cluster: + expose-ingress: tenant-root + expose-external-ips: "192.0.2.10/32,2001:db8::1/128" + expose-mode: loadBalancer + asserts: + - equal: + path: spec.blocks + value: + - cidr: 192.0.2.10/32 + - cidr: 2001:db8::1/128 + + - it: loadBalancer mode filters out empty entries from externalIPs (trailing, leading, repeated commas) + set: + ingress: true + _cluster: + expose-ingress: tenant-root + expose-external-ips: "192.0.2.10,,192.0.2.11," + expose-mode: loadBalancer + asserts: + - hasDocuments: + count: 1 + - equal: + path: spec.blocks + value: + - cidr: 192.0.2.10/32 + - cidr: 192.0.2.11/32 + + - it: loadBalancer mode with ingress=false in publishing tenant renders no pool + set: + ingress: false + _cluster: + expose-ingress: tenant-root + expose-external-ips: "192.0.2.10" + expose-mode: loadBalancer + asserts: + - hasDocuments: + count: 0 + + - it: loadBalancer mode in a non-publishing tenant renders no pool + set: + ingress: true + _cluster: + expose-ingress: tenant-root + expose-external-ips: "192.0.2.10" + expose-mode: loadBalancer + release: + name: tenant-u1 + namespace: tenant-u1 + asserts: + - hasDocuments: + count: 0 + + - it: loadBalancer mode in publishing tenant with empty externalIPs renders no pool + set: + ingress: true + _cluster: + expose-ingress: tenant-root + expose-external-ips: "" + expose-mode: loadBalancer + asserts: + - hasDocuments: + count: 0 diff --git a/packages/apps/tenant/values.schema.json b/packages/apps/tenant/values.schema.json index 2c3d5f89..28d9ac1e 100644 --- a/packages/apps/tenant/values.schema.json +++ b/packages/apps/tenant/values.schema.json @@ -2,18 +2,13 @@ "title": "Chart Values", "type": "object", "properties": { - "etcd": { - "description": "Deploy own Etcd cluster.", - "type": "boolean", - "default": false - }, "host": { "description": "The hostname used to access tenant services (defaults to using the tenant name as a subdomain for its parent tenant host).", "type": "string", "default": "" }, - "ingress": { - "description": "Deploy own Ingress Controller.", + "etcd": { + "description": "Deploy own Etcd cluster.", "type": "boolean", "default": false }, @@ -22,6 +17,21 @@ "type": "boolean", "default": false }, + "ingress": { + "description": "Deploy own Ingress Controller.", + "type": "boolean", + "default": false + }, + "seaweedfs": { + "description": "Deploy own SeaweedFS.", + "type": "boolean", + "default": false + }, + "schedulingClass": { + "description": "The name of a SchedulingClass CR to apply scheduling constraints for this tenant's workloads.", + "type": "string", + "default": "" + }, "resourceQuotas": { "description": "Define resource quotas for the tenant.", "type": "object", @@ -38,11 +48,6 @@ ], "x-kubernetes-int-or-string": true } - }, - "seaweedfs": { - "description": "Deploy own SeaweedFS.", - "type": "boolean", - "default": false } } -} \ No newline at end of file +} diff --git a/packages/apps/tenant/values.yaml b/packages/apps/tenant/values.yaml index 58bb451f..f9f8a999 100644 --- a/packages/apps/tenant/values.yaml +++ b/packages/apps/tenant/values.yaml @@ -17,5 +17,8 @@ ingress: false ## @param {bool} seaweedfs - Deploy own SeaweedFS. seaweedfs: false +## @param {string} [schedulingClass] - The name of a SchedulingClass CR to apply scheduling constraints for this tenant's workloads. +schedulingClass: "" + ## @param {map[string]quantity} resourceQuotas - Define resource quotas for the tenant. resourceQuotas: {} diff --git a/packages/apps/vm-disk/Makefile b/packages/apps/vm-disk/Makefile index d1cfda8e..80b1075a 100644 --- a/packages/apps/vm-disk/Makefile +++ b/packages/apps/vm-disk/Makefile @@ -1,5 +1,5 @@ include ../../../hack/package.mk generate: - cozyvalues-gen -v values.yaml -s values.schema.json -r README.md + cozyvalues-gen -m 'vmdisk' -v values.yaml -s values.schema.json -r README.md -g ../../../api/apps/v1alpha1/vmdisk/types.go ../../../hack/update-crd.sh diff --git a/packages/apps/vm-disk/README.md b/packages/apps/vm-disk/README.md index b8a4cdac..3727d0f5 100644 --- a/packages/apps/vm-disk/README.md +++ b/packages/apps/vm-disk/README.md @@ -6,15 +6,17 @@ A Virtual Machine Disk ### Common parameters -| Name | Description | Type | Value | -| ------------------- | ------------------------------------------------------------------------------------------------------------------------ | ---------- | ------------ | -| `source` | The source image location used to create a disk. | `object` | `{}` | -| `source.image` | Use image by name. | `*object` | `null` | -| `source.image.name` | Name of the image to use (uploaded as "golden image" or from the list: `ubuntu`, `fedora`, `cirros`, `alpine`, `talos`). | `string` | `""` | -| `source.upload` | Upload local image. | `*object` | `null` | -| `source.http` | Download image from an HTTP source. | `*object` | `null` | -| `source.http.url` | URL to download the image. | `string` | `""` | -| `optical` | Defines if disk should be considered optical. | `bool` | `false` | -| `storage` | The size of the disk allocated for the virtual machine. | `quantity` | `5Gi` | -| `storageClass` | StorageClass used to store the data. | `string` | `replicated` | +| Name | Description | Type | Value | +| ------------------- | ------------------------------------------------------- | ---------- | ------------ | +| `source` | The source image location used to create a disk. | `object` | `{}` | +| `source.image` | Use image by name from default collection. | `*object` | `null` | +| `source.image.name` | Name of the image to use. | `string` | `""` | +| `source.upload` | Upload local image. | `*object` | `null` | +| `source.http` | Download image from an HTTP source. | `*object` | `null` | +| `source.http.url` | URL to download the image. | `string` | `""` | +| `source.disk` | Clone an existing vm-disk. | `*object` | `null` | +| `source.disk.name` | Name of the vm-disk to clone. | `string` | `""` | +| `optical` | Defines if disk should be considered optical. | `bool` | `false` | +| `storage` | The size of the disk allocated for the virtual machine. | `quantity` | `5Gi` | +| `storageClass` | StorageClass used to store the data. | `string` | `replicated` | diff --git a/packages/apps/vm-disk/templates/dv.yaml b/packages/apps/vm-disk/templates/dv.yaml index 3d68e639..521a82b8 100644 --- a/packages/apps/vm-disk/templates/dv.yaml +++ b/packages/apps/vm-disk/templates/dv.yaml @@ -21,15 +21,18 @@ spec: {{- end }} source: {{- if hasKey .Values.source "image" }} - {{- $dv := lookup "cdi.kubevirt.io/v1beta1" "DataVolume" "cozy-public" (printf "vm-image-%s" .Values.source.image.name) }} pvc: - name: vm-image-{{ required "A valid .Values.source.image.name entry required!" .Values.source.image.name }} + name: vm-default-images-{{ required "A valid .Values.source.image.name entry required!" .Values.source.image.name }} namespace: cozy-public {{- else if hasKey .Values.source "http" }} http: url: {{ required "A valid .Values.source.http.url entry required!" .Values.source.http.url }} {{- else if hasKey .Values.source "upload" }} upload: {} + {{- else if hasKey .Values.source "disk" }} + pvc: + name: vm-disk-{{ required "A valid .Values.source.disk.name entry required!" .Values.source.disk.name }} + namespace: {{ $.Release.Namespace }} {{- end }} {{- else }} source: diff --git a/packages/apps/vm-disk/values.schema.json b/packages/apps/vm-disk/values.schema.json index cd2215d8..d61fc359 100644 --- a/packages/apps/vm-disk/values.schema.json +++ b/packages/apps/vm-disk/values.schema.json @@ -2,16 +2,24 @@ "title": "Chart Values", "type": "object", "properties": { - "optical": { - "description": "Defines if disk should be considered optical.", - "type": "boolean", - "default": false - }, "source": { "description": "The source image location used to create a disk.", "type": "object", "default": {}, "properties": { + "disk": { + "description": "Clone an existing vm-disk.", + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "description": "Name of the vm-disk to clone.", + "type": "string" + } + } + }, "http": { "description": "Download image from an HTTP source.", "type": "object", @@ -26,23 +34,29 @@ } }, "image": { - "description": "Use image by name.", + "description": "Use image by name from default collection.", "type": "object", "required": [ "name" ], "properties": { "name": { - "description": "Name of the image to use (uploaded as \"golden image\" or from the list: `ubuntu`, `fedora`, `cirros`, `alpine`, `talos`).", + "description": "Name of the image to use.", "type": "string" } } }, "upload": { - "description": "Upload local image." + "description": "Upload local image.", + "type": "object" } } }, + "optical": { + "description": "Defines if disk should be considered optical.", + "type": "boolean", + "default": false + }, "storage": { "description": "The size of the disk allocated for the virtual machine.", "default": "5Gi", @@ -63,4 +77,4 @@ "default": "replicated" } } -} \ No newline at end of file +} diff --git a/packages/apps/vm-disk/values.yaml b/packages/apps/vm-disk/values.yaml index 33bd174f..b9a20d68 100644 --- a/packages/apps/vm-disk/values.yaml +++ b/packages/apps/vm-disk/values.yaml @@ -3,17 +3,21 @@ ## ## @typedef {struct} SourceImage - Use image by name. -## @field {string} name - Name of the image to use (uploaded as "golden image" or from the list: `ubuntu`, `fedora`, `cirros`, `alpine`, `talos`). +## @field {string} name - Name of the image to use. ## @typedef {struct} SourceUpload - Upload local image. ## @typedef {struct} SourceHTTP - Download image from an HTTP source. ## @field {string} url - URL to download the image. +## @typedef {struct} SourceDisk - Clone an existing vm-disk. +## @field {string} name - Name of the vm-disk to clone. + ## @typedef {struct} Source - The source image location used to create a disk. -## @field {*SourceImage} [image] - Use image by name. +## @field {*SourceImage} [image] - Use image by name from default collection. ## @field {*SourceUpload} [upload] - Upload local image. ## @field {*SourceHTTP} [http] - Download image from an HTTP source. +## @field {*SourceDisk} [disk] - Clone an existing vm-disk. ## @param {Source} source - The source image location used to create a disk. source: {} diff --git a/packages/apps/vm-instance/Makefile b/packages/apps/vm-instance/Makefile index 3b6d471d..92798e4f 100644 --- a/packages/apps/vm-instance/Makefile +++ b/packages/apps/vm-instance/Makefile @@ -1,7 +1,7 @@ include ../../../hack/package.mk generate: - cozyvalues-gen -v values.yaml -s values.schema.json -r README.md + cozyvalues-gen -m 'vminstance' -v values.yaml -s values.schema.json -r README.md -g ../../../api/apps/v1alpha1/vminstance/types.go #INSTANCE_TYPES=$$(yq e '.metadata.name' -o=json -r ../../system/kubevirt-instancetypes/templates/instancetypes.yaml | yq 'split(" ") | . + [""]' -o json) \ # && yq -i -o json ".properties.instanceType.enum = $${INSTANCE_TYPES}" values.schema.json PREFERENCES=$$(yq e '.metadata.name' -o=json -r ../../system/kubevirt-instancetypes/templates/preferences.yaml | yq 'split(" ") | . + [""]' -o json) \ diff --git a/packages/apps/vm-instance/README.md b/packages/apps/vm-instance/README.md index 11d1441d..9d6a52b2 100644 --- a/packages/apps/vm-instance/README.md +++ b/packages/apps/vm-instance/README.md @@ -36,29 +36,32 @@ virtctl ssh @ ### Common parameters -| Name | Description | Type | Value | -| ------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ---------- | ----------- | -| `external` | Enable external access from outside the cluster. | `bool` | `false` | -| `externalMethod` | Method to pass through traffic to the VM. | `string` | `PortList` | -| `externalPorts` | Ports to forward from outside the cluster. | `[]int` | `[22]` | -| `runStrategy` | Requested running state of the VirtualMachineInstance | `string` | `Always` | -| `instanceType` | Virtual Machine instance type. | `string` | `u1.medium` | -| `instanceProfile` | Virtual Machine preferences profile. | `string` | `ubuntu` | -| `disks` | List of disks to attach. | `[]object` | `[]` | -| `disks[i].name` | Disk name. | `string` | `""` | -| `disks[i].bus` | Disk bus type (e.g. "sata"). | `string` | `""` | -| `subnets` | Additional subnets | `[]object` | `[]` | -| `subnets[i].name` | Subnet name | `string` | `""` | -| `gpus` | List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM). | `[]object` | `[]` | -| `gpus[i].name` | The name of the GPU resource to attach. | `string` | `""` | -| `cpuModel` | Model specifies the CPU model inside the VMI. List of available models https://github.com/libvirt/libvirt/tree/master/src/cpu_map | `string` | `""` | -| `resources` | Resource configuration for the virtual machine. | `object` | `{}` | -| `resources.cpu` | Number of CPU cores allocated. | `quantity` | `""` | -| `resources.memory` | Amount of memory allocated. | `quantity` | `""` | -| `resources.sockets` | Number of CPU sockets (vCPU topology). | `quantity` | `""` | -| `sshKeys` | List of SSH public keys for authentication. | `[]string` | `[]` | -| `cloudInit` | Cloud-init user data. | `string` | `""` | -| `cloudInitSeed` | Seed string to generate SMBIOS UUID for the VM. | `string` | `""` | +| Name | Description | Type | Value | +| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ----------- | +| `external` | Enable external access from outside the cluster. | `bool` | `false` | +| `externalMethod` | Method to pass through traffic to the VM. | `string` | `PortList` | +| `externalPorts` | Ports to forward from outside the cluster. | `[]int` | `[22]` | +| `externalAllowICMP` | Whether to accept ICMP traffic to the VM in PortList mode (preserves ping and PMTU discovery). No effect in WholeIP mode. Default true so ping behaves as users expect even when port filtering is in effect. | `bool` | `true` | +| `runStrategy` | Requested running state of the VirtualMachineInstance | `string` | `Always` | +| `instanceType` | Virtual Machine instance type. | `string` | `u1.medium` | +| `instanceProfile` | Virtual Machine preferences profile. | `string` | `ubuntu` | +| `disks` | List of disks to attach. | `[]object` | `[]` | +| `disks[i].name` | Disk name. | `string` | `""` | +| `disks[i].bus` | Disk bus type (e.g. "sata"). | `string` | `""` | +| `networks` | Networks to attach the VM to. | `[]object` | `[]` | +| `networks[i].name` | Network attachment name. | `string` | `""` | +| `subnets` | Deprecated: use networks instead. | `[]object` | `[]` | +| `subnets[i].name` | Network attachment name. | `string` | `""` | +| `gpus` | List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM). | `[]object` | `[]` | +| `gpus[i].name` | The name of the GPU resource to attach. | `string` | `""` | +| `cpuModel` | Model specifies the CPU model inside the VMI. List of available models https://github.com/libvirt/libvirt/tree/master/src/cpu_map | `string` | `""` | +| `resources` | Resource configuration for the virtual machine. | `object` | `{}` | +| `resources.cpu` | Number of CPU cores allocated. | `quantity` | `""` | +| `resources.memory` | Amount of memory allocated. | `quantity` | `""` | +| `resources.sockets` | Number of CPU sockets (vCPU topology). | `quantity` | `""` | +| `sshKeys` | List of SSH public keys for authentication. | `[]string` | `[]` | +| `cloudInit` | Cloud-init user data. | `string` | `""` | +| `cloudInitSeed` | Seed string to generate SMBIOS UUID for the VM. | `string` | `""` | ## U Series diff --git a/packages/apps/vm-instance/templates/service.yaml b/packages/apps/vm-instance/templates/service.yaml index b12f7612..cfeffa81 100644 --- a/packages/apps/vm-instance/templates/service.yaml +++ b/packages/apps/vm-instance/templates/service.yaml @@ -7,8 +7,12 @@ metadata: apps.cozystack.io/user-service: "true" {{- include "virtual-machine.labels" . | nindent 4 }} {{- if .Values.external }} + service.kubernetes.io/service-proxy-name: "cozy-proxy" annotations: - networking.cozystack.io/wholeIP: "true" + networking.cozystack.io/wholeIP: {{ ternary "true" "false" (eq .Values.externalMethod "WholeIP") | quote }} + {{- if eq .Values.externalMethod "PortList" }} + networking.cozystack.io/allowICMP: {{ ternary "true" "false" (ne .Values.externalAllowICMP false) | quote }} + {{- end }} {{- end }} spec: type: {{ ternary "LoadBalancer" "ClusterIP" .Values.external }} diff --git a/packages/apps/vm-instance/templates/vm.yaml b/packages/apps/vm-instance/templates/vm.yaml index 226e5aa7..55f278c1 100644 --- a/packages/apps/vm-instance/templates/vm.yaml +++ b/packages/apps/vm-instance/templates/vm.yaml @@ -7,6 +7,7 @@ {{- if and (not .Values.instanceType) (not (and .Values.resources .Values.resources.cpu .Values.resources.sockets .Values.resources.memory)) }} {{- fail "Either instanceType or resources (cpu, sockets, memory) must be specified" }} {{- end }} +{{- $networks := .Values.networks | default .Values.subnets }} apiVersion: kubevirt.io/v1 kind: VirtualMachine @@ -34,6 +35,12 @@ spec: metadata: annotations: kubevirt.io/allow-pod-bridge-network-live-migration: "true" + {{- $ovnIPName := printf "%s.%s" (include "virtual-machine.fullname" .) .Release.Namespace }} + {{- $ovnIP := lookup "kubeovn.io/v1" "IP" "" $ovnIPName }} + {{- if $ovnIP }} + ovn.kubernetes.io/mac_address: {{ $ovnIP.spec.macAddress | quote }} + ovn.kubernetes.io/ip_address: {{ $ovnIP.spec.ipAddress | quote }} + {{- end }} labels: {{- include "virtual-machine.labels" . | nindent 8 }} spec: @@ -80,12 +87,10 @@ spec: interfaces: - name: default bridge: {} - {{- if .Values.subnets }} - {{- range $i, $subnet := .Values.subnets }} - - name: {{ $subnet.name }} + {{- range $i, $net := $networks }} + - name: {{ $net.name }} bridge: {} {{- end }} - {{- end }} machine: type: "" {{- with .Values.sshKeys }} @@ -117,10 +122,8 @@ spec: networks: - name: default pod: {} - {{- if .Values.subnets }} - {{- range $i, $subnet := .Values.subnets }} - - name: {{ $subnet.name }} + {{- range $i, $net := $networks }} + - name: {{ $net.name }} multus: - networkName: {{ $.Release.Namespace }}/{{ $subnet.name }} - {{- end }} + networkName: {{ $.Release.Namespace }}/{{ $net.name }} {{- end }} diff --git a/packages/apps/vm-instance/values.schema.json b/packages/apps/vm-instance/values.schema.json index 0788c879..01bd30a9 100644 --- a/packages/apps/vm-instance/values.schema.json +++ b/packages/apps/vm-instance/values.schema.json @@ -2,42 +2,6 @@ "title": "Chart Values", "type": "object", "properties": { - "cloudInit": { - "description": "Cloud-init user data.", - "type": "string", - "default": "" - }, - "cloudInitSeed": { - "description": "Seed string to generate SMBIOS UUID for the VM.", - "type": "string", - "default": "" - }, - "cpuModel": { - "description": "Model specifies the CPU model inside the VMI. List of available models https://github.com/libvirt/libvirt/tree/master/src/cpu_map", - "type": "string", - "default": "" - }, - "disks": { - "description": "List of disks to attach.", - "type": "array", - "default": [], - "items": { - "type": "object", - "required": [ - "name" - ], - "properties": { - "bus": { - "description": "Disk bus type (e.g. \"sata\").", - "type": "string" - }, - "name": { - "description": "Disk name.", - "type": "string" - } - } - } - }, "external": { "description": "Enable external access from outside the cluster.", "type": "boolean", @@ -62,22 +26,27 @@ "type": "integer" } }, - "gpus": { - "description": "List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM).", - "type": "array", - "default": [], - "items": { - "type": "object", - "required": [ - "name" - ], - "properties": { - "name": { - "description": "The name of the GPU resource to attach.", - "type": "string" - } - } - } + "externalAllowICMP": { + "description": "Whether to accept ICMP traffic to the VM in PortList mode (preserves ping and PMTU discovery). No effect in WholeIP mode. Default true so ping behaves as users expect even when port filtering is in effect.", + "type": "boolean", + "default": true + }, + "runStrategy": { + "description": "Requested running state of the VirtualMachineInstance", + "type": "string", + "default": "Always", + "enum": [ + "Always", + "Halted", + "Manual", + "RerunOnFailure", + "Once" + ] + }, + "instanceType": { + "description": "Virtual Machine instance type.", + "type": "string", + "default": "u1.medium" }, "instanceProfile": { "description": "Virtual Machine preferences profile.", @@ -129,10 +98,76 @@ "" ] }, - "instanceType": { - "description": "Virtual Machine instance type.", + "disks": { + "description": "List of disks to attach.", + "type": "array", + "default": [], + "items": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "bus": { + "description": "Disk bus type (e.g. \"sata\").", + "type": "string" + }, + "name": { + "description": "Disk name.", + "type": "string" + } + } + } + }, + "networks": { + "description": "Networks to attach the VM to.", + "type": "array", + "default": [], + "items": { + "type": "object", + "properties": { + "name": { + "description": "Network attachment name.", + "type": "string" + } + } + } + }, + "subnets": { + "description": "Deprecated: use networks instead.", + "type": "array", + "default": [], + "items": { + "type": "object", + "properties": { + "name": { + "description": "Network attachment name.", + "type": "string" + } + } + } + }, + "gpus": { + "description": "List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM).", + "type": "array", + "default": [], + "items": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "description": "The name of the GPU resource to attach.", + "type": "string" + } + } + } + }, + "cpuModel": { + "description": "Model specifies the CPU model inside the VMI. List of available models https://github.com/libvirt/libvirt/tree/master/src/cpu_map", "type": "string", - "default": "u1.medium" + "default": "" }, "resources": { "description": "Resource configuration for the virtual machine.", @@ -180,18 +215,6 @@ } } }, - "runStrategy": { - "description": "Requested running state of the VirtualMachineInstance", - "type": "string", - "default": "Always", - "enum": [ - "Always", - "Halted", - "Manual", - "RerunOnFailure", - "Once" - ] - }, "sshKeys": { "description": "List of SSH public keys for authentication.", "type": "array", @@ -200,19 +223,15 @@ "type": "string" } }, - "subnets": { - "description": "Additional subnets", - "type": "array", - "default": [], - "items": { - "type": "object", - "properties": { - "name": { - "description": "Subnet name", - "type": "string" - } - } - } + "cloudInit": { + "description": "Cloud-init user data.", + "type": "string", + "default": "" + }, + "cloudInitSeed": { + "description": "Seed string to generate SMBIOS UUID for the VM.", + "type": "string", + "default": "" } } } diff --git a/packages/apps/vm-instance/values.yaml b/packages/apps/vm-instance/values.yaml index d5ed5679..92e399c2 100644 --- a/packages/apps/vm-instance/values.yaml +++ b/packages/apps/vm-instance/values.yaml @@ -10,8 +10,8 @@ ## @field {string} name - Disk name. ## @field {string} [bus] - Disk bus type (e.g. "sata"). -## @typedef {struct} Subnet - Additional subnets -## @field {string} [name] - Subnet name +## @typedef {struct} Network - Network to attach the VM to. +## @field {string} [name] - Network attachment name. ## @typedef {struct} GPU - GPU device configuration. ## @field {string} name - The name of the GPU resource to attach. @@ -31,6 +31,9 @@ externalMethod: PortList externalPorts: - 22 +## @param {bool} externalAllowICMP - Whether to accept ICMP traffic to the VM in PortList mode (preserves ping and PMTU discovery). No effect in WholeIP mode. Default true so ping behaves as users expect even when port filtering is in effect. +externalAllowICMP: true + ## @enum {string} RunStrategy - Requested running state of the VirtualMachineInstance ## @value Always - VMI should always be running ## @value Halted - VMI should never be running @@ -55,13 +58,15 @@ disks: [] ## - name: example-data ## bus: sata -## @param {[]Subnet} subnets - Additional subnets -subnets: [] +## @param {[]Network} networks - Networks to attach the VM to. +networks: [] ## Example: -## subnets: +## networks: ## - name: subnet-84dbec17 ## - name: subnet-aa8896b5 -## - name: subnet-e9b97196 + +## @param {[]Network} subnets - Deprecated: use networks instead. +subnets: [] ## @param {[]GPU} gpus - List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM). gpus: [] diff --git a/packages/apps/vpc/Makefile b/packages/apps/vpc/Makefile index ea4f5d1b..2f0c193f 100644 --- a/packages/apps/vpc/Makefile +++ b/packages/apps/vpc/Makefile @@ -1,7 +1,7 @@ include ../../../hack/package.mk generate: - cozyvalues-gen -v values.yaml -s values.schema.json + cozyvalues-gen -m 'vpc' -v values.yaml -s values.schema.json -g ../../../api/apps/v1alpha1/vpc/types.go ../../../hack/update-crd.sh update: diff --git a/packages/apps/vpc/templates/vpc.yaml b/packages/apps/vpc/templates/vpc.yaml index 6c0d2249..784c181b 100644 --- a/packages/apps/vpc/templates/vpc.yaml +++ b/packages/apps/vpc/templates/vpc.yaml @@ -15,6 +15,34 @@ spec: enableExternal: false namespaces: - {{ .Release.Namespace }} +{{- if .Values.peers }} + vpcPeerings: +{{- range .Values.peers }} +{{- $remoteRelease := printf "virtualprivatecloud-%s" .vpcName }} +{{- $remoteVpcId := printf "vpc-%s" (printf "%s/%s" .tenantNamespace $remoteRelease | sha256sum | trunc 6) }} +{{- $sorted := list $vpcId $remoteVpcId | sortAlpha }} +{{- $pairKey := join "/" $sorted }} +{{- $pairHash := sha256sum $pairKey }} +{{- $byte0 := int (include "cozy-lib.strings.hexToInt" (substr 0 2 $pairHash)) }} +{{- $byte1 := int (include "cozy-lib.strings.hexToInt" (substr 2 4 $pairHash)) }} +{{- $oct3 := int (add (mod $byte0 254) 1) }} +{{- $base4 := int (mul (mod $byte1 64) 4) }} +{{- if eq $vpcId (index $sorted 0) }} + - remoteVpc: {{ $remoteVpcId }} + localConnectIP: {{ printf "169.254.%d.%d" $oct3 (int (add $base4 1)) }} +{{- else }} + - remoteVpc: {{ $remoteVpcId }} + localConnectIP: {{ printf "169.254.%d.%d" $oct3 (int (add $base4 2)) }} +{{- end }} +{{- end }} +{{- end }} +{{- if .Values.routes }} + staticRoutes: +{{- range .Values.routes }} + - cidr: {{ .cidr | quote }} + nextHopIP: {{ .nextHopIP | quote }} +{{- end }} +{{- end }} {{- range .Values.subnets }} {{- $subnetId := print "subnet-" (print $.Release.Namespace "/" $vpcId "/" .name | sha256sum | trunc 8) }} @@ -70,6 +98,12 @@ data: {{ .name }}.ID: {{ print "subnet-" (print $.Release.Namespace "/" $vpcId "/" .name | sha256sum | trunc 8) }} {{ .name }}.CIDR: {{ .cidr }} {{- end }} + {{- range .Values.peers }} + {{- $remoteRelease := printf "virtualprivatecloud-%s" .vpcName }} + {{- $remoteVpcId := printf "vpc-%s" (printf "%s/%s" .tenantNamespace $remoteRelease | sha256sum | trunc 6) }} + peer.{{ .tenantNamespace }}.{{ .vpcName }}.vpcId: {{ $remoteVpcId | quote }} + peer.{{ .tenantNamespace }}.{{ .vpcName }}.tenantNamespace: {{ .tenantNamespace | quote }} + {{- end }} --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding diff --git a/packages/apps/vpc/values.schema.json b/packages/apps/vpc/values.schema.json index a975d2d7..b25b46ed 100644 --- a/packages/apps/vpc/values.schema.json +++ b/packages/apps/vpc/values.schema.json @@ -22,6 +22,50 @@ } } } + }, + "peers": { + "description": "VPC peering connections (bidirectional declaration required)", + "type": "array", + "default": [], + "items": { + "type": "object", + "required": [ + "tenantNamespace", + "vpcName" + ], + "properties": { + "tenantNamespace": { + "description": "Namespace of the remote tenant", + "type": "string" + }, + "vpcName": { + "description": "Logical name of the remote VPC (without \"virtualprivatecloud-\" prefix)", + "type": "string" + } + } + } + }, + "routes": { + "description": "Static routes for the VPC", + "type": "array", + "default": [], + "items": { + "type": "object", + "required": [ + "cidr", + "nextHopIP" + ], + "properties": { + "cidr": { + "description": "Destination CIDR", + "type": "string" + }, + "nextHopIP": { + "description": "Next hop IP address", + "type": "string" + } + } + } } } -} \ No newline at end of file +} diff --git a/packages/apps/vpc/values.yaml b/packages/apps/vpc/values.yaml index 3af8b26b..8bd27589 100644 --- a/packages/apps/vpc/values.yaml +++ b/packages/apps/vpc/values.yaml @@ -14,3 +14,25 @@ subnets: [] ## cidr: "172.16.0.0/24" ## - name: mysubnet1 ## cidr: "172.16.1.0/24" + +## @typedef {struct} Peer - VPC peering target +## @field {string} vpcName - Logical name of the remote VPC (without "virtualprivatecloud-" prefix) +## @field {string} tenantNamespace - Namespace of the remote tenant + +## @param {[]Peer} peers - VPC peering connections (bidirectional declaration required) +peers: [] +## Example: +## peers: +## - vpcName: "other-vpc" +## tenantNamespace: "tenant-other" + +## @typedef {struct} Route - Static route entry +## @field {string} cidr - Destination CIDR +## @field {string} nextHopIP - Next hop IP address + +## @param {[]Route} routes - Static routes for the VPC +routes: [] +## Example: +## routes: +## - cidr: "172.16.1.0/24" +## nextHopIP: "10.0.0.1" diff --git a/packages/apps/vpn/Makefile b/packages/apps/vpn/Makefile index d1cfda8e..d0b8412f 100644 --- a/packages/apps/vpn/Makefile +++ b/packages/apps/vpn/Makefile @@ -1,5 +1,5 @@ include ../../../hack/package.mk generate: - cozyvalues-gen -v values.yaml -s values.schema.json -r README.md + cozyvalues-gen -m 'vpn' -v values.yaml -s values.schema.json -r README.md -g ../../../api/apps/v1alpha1/vpn/types.go ../../../hack/update-crd.sh diff --git a/packages/apps/vpn/templates/workloadmonitor.yaml b/packages/apps/vpn/templates/workloadmonitor.yaml index a75f7940..7fc1ec7a 100644 --- a/packages/apps/vpn/templates/workloadmonitor.yaml +++ b/packages/apps/vpn/templates/workloadmonitor.yaml @@ -2,6 +2,8 @@ apiVersion: cozystack.io/v1alpha1 kind: WorkloadMonitor metadata: name: {{ $.Release.Name }} + labels: + workloads.cozystack.io/resource-preset: {{ .Values.resourcesPreset | quote }} spec: replicas: {{ .Values.replicas }} minReplicas: 1 diff --git a/packages/apps/vpn/values.schema.json b/packages/apps/vpn/values.schema.json index 14e85262..abfd7b52 100644 --- a/packages/apps/vpn/values.schema.json +++ b/packages/apps/vpn/values.schema.json @@ -2,24 +2,6 @@ "title": "Chart Values", "type": "object", "properties": { - "external": { - "description": "Enable external access from outside the cluster.", - "type": "boolean", - "default": false - }, - "externalIPs": { - "description": "List of externalIPs for service. Optional. If not specified, will use LoadBalancer service by default.", - "type": "array", - "default": [], - "items": { - "type": "string" - } - }, - "host": { - "description": "Host used to substitute into generated URLs.", - "type": "string", - "default": "" - }, "replicas": { "description": "Number of VPN server replicas.", "type": "integer", @@ -72,6 +54,16 @@ "2xlarge" ] }, + "external": { + "description": "Enable external access from outside the cluster.", + "type": "boolean", + "default": false + }, + "host": { + "description": "Host used to substitute into generated URLs.", + "type": "string", + "default": "" + }, "users": { "description": "Users configuration map.", "type": "object", @@ -85,6 +77,14 @@ } } } + }, + "externalIPs": { + "description": "List of externalIPs for service. Optional. If not specified, will use LoadBalancer service by default.", + "type": "array", + "default": [], + "items": { + "type": "string" + } } } -} \ No newline at end of file +} diff --git a/packages/core/installer/Makefile b/packages/core/installer/Makefile index 7038bb9c..a661bed4 100644 --- a/packages/core/installer/Makefile +++ b/packages/core/installer/Makefile @@ -31,6 +31,7 @@ image-operator: image-packages: mkdir -p ../../../_out/assets images + echo $$(git rev-parse HEAD; git diff HEAD; git ls-files --others --exclude-standard) | shasum -a 256 > ../platform/.build-revision flux push artifact \ oci://$(REGISTRY)/cozystack-packages:$(call settag,$(TAG)) \ --path=../../../packages \ diff --git a/packages/core/installer/crds/.gitattributes b/packages/core/installer/crds/.gitattributes deleted file mode 100644 index f581feca..00000000 --- a/packages/core/installer/crds/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -*.yaml linguist-generated diff --git a/packages/core/installer/crds/cozystack.io_packages.yaml b/packages/core/installer/crds/cozystack.io_packages.yaml deleted file mode 100644 index cb7e2763..00000000 --- a/packages/core/installer/crds/cozystack.io_packages.yaml +++ /dev/null @@ -1,171 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.16.4 - name: packages.cozystack.io -spec: - group: cozystack.io - names: - kind: Package - listKind: PackageList - plural: packages - shortNames: - - pkg - - pkgs - singular: package - scope: Cluster - versions: - - additionalPrinterColumns: - - description: Selected variant - jsonPath: .spec.variant - name: Variant - type: string - - description: Ready status - jsonPath: .status.conditions[?(@.type=='Ready')].status - name: Ready - type: string - - description: Ready message - jsonPath: .status.conditions[?(@.type=='Ready')].message - name: Status - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: Package is the Schema for the packages API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: PackageSpec defines the desired state of Package - properties: - components: - additionalProperties: - description: PackageComponent defines overrides for a specific component - properties: - enabled: - description: |- - Enabled indicates whether this component should be installed - If false, the component will be disabled even if it's defined in the PackageSource - type: boolean - values: - description: |- - Values contains Helm chart values as a JSON object - These values will be merged with the default values from the PackageSource - x-kubernetes-preserve-unknown-fields: true - type: object - description: |- - Components is a map of release name to component overrides - Allows overriding values and enabling/disabling specific components from the PackageSource - type: object - ignoreDependencies: - description: |- - IgnoreDependencies is a list of package source dependencies to ignore - Dependencies listed here will not be installed even if they are specified in the PackageSource - items: - type: string - type: array - variant: - description: |- - Variant is the name of the variant to use from the PackageSource - If not specified, defaults to "default" - type: string - type: object - status: - description: PackageStatus defines the observed state of Package - properties: - conditions: - description: Conditions represents the latest available observations - of a Package's state - items: - description: Condition contains details for one aspect of the current - state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - dependencies: - additionalProperties: - description: DependencyStatus represents the readiness status of - a dependency - properties: - ready: - description: Ready indicates whether the dependency is ready - type: boolean - required: - - ready - type: object - description: |- - Dependencies tracks the readiness status of each dependency - Key is the dependency package name, value indicates if the dependency is ready - type: object - type: object - type: object - served: true - storage: true - subresources: - status: {} diff --git a/packages/core/installer/crds/cozystack.io_packagesources.yaml b/packages/core/installer/crds/cozystack.io_packagesources.yaml deleted file mode 100644 index 0acfcdd2..00000000 --- a/packages/core/installer/crds/cozystack.io_packagesources.yaml +++ /dev/null @@ -1,250 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.16.4 - name: packagesources.cozystack.io -spec: - group: cozystack.io - names: - kind: PackageSource - listKind: PackageSourceList - plural: packagesources - shortNames: - - pks - singular: packagesource - scope: Cluster - versions: - - additionalPrinterColumns: - - description: Package variants (comma-separated) - jsonPath: .status.variants - name: Variants - type: string - - description: Ready status - jsonPath: .status.conditions[?(@.type=='Ready')].status - name: Ready - type: string - - description: Ready message - jsonPath: .status.conditions[?(@.type=='Ready')].message - name: Status - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: PackageSource is the Schema for the packagesources API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: PackageSourceSpec defines the desired state of PackageSource - properties: - sourceRef: - description: SourceRef is the source reference for the package source - charts - properties: - kind: - description: Kind of the source reference - enum: - - GitRepository - - OCIRepository - type: string - name: - description: Name of the source reference - type: string - namespace: - description: Namespace of the source reference - type: string - path: - description: |- - Path is the base path where packages are located in the source. - For GitRepository, defaults to "packages" if not specified. - For OCIRepository, defaults to empty string (root) if not specified. - type: string - required: - - kind - - name - - namespace - type: object - variants: - description: |- - Variants is a list of package source variants - Each variant defines components, applications, dependencies, and libraries for a specific configuration - items: - description: Variant defines a single variant configuration - properties: - components: - description: Components is a list of Helm releases to be installed - as part of this variant - items: - description: Component defines a single Helm release component - within a package source - properties: - install: - description: Install defines installation parameters for - this component - properties: - dependsOn: - description: DependsOn is a list of component names - that must be installed before this component - items: - type: string - type: array - namespace: - description: Namespace is the Kubernetes namespace - where the release will be installed - type: string - privileged: - description: Privileged indicates whether this release - requires privileged access - type: boolean - releaseName: - description: |- - ReleaseName is the name of the HelmRelease resource that will be created - If not specified, defaults to the component Name field - type: string - type: object - libraries: - description: |- - Libraries is a list of library names that this component depends on - These libraries must be defined at the variant level - items: - type: string - type: array - name: - description: Name is the unique identifier for this component - within the package source - type: string - path: - description: Path is the path to the Helm chart directory - type: string - valuesFiles: - description: ValuesFiles is a list of values file names - to use - items: - type: string - type: array - required: - - name - - path - type: object - type: array - dependsOn: - description: |- - DependsOn is a list of package source dependencies - For example: "cozystack.networking" - items: - type: string - type: array - libraries: - description: Libraries is a list of Helm library charts used - by components in this variant - items: - description: Library defines a Helm library chart - properties: - name: - description: Name is the optional name for library placed - in charts - type: string - path: - description: Path is the path to the library chart directory - type: string - required: - - path - type: object - type: array - name: - description: Name is the unique identifier for this variant - type: string - required: - - name - type: object - type: array - type: object - status: - description: PackageSourceStatus defines the observed state of PackageSource - properties: - conditions: - description: Conditions represents the latest available observations - of a PackageSource's state - items: - description: Condition contains details for one aspect of the current - state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - variants: - description: |- - Variants is a comma-separated list of package variant names - This field is populated by the controller based on spec.variants keys - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} diff --git a/packages/core/installer/templates/cozystack-operator.yaml b/packages/core/installer/templates/cozystack-operator.yaml index 5cd471cf..aded0995 100644 --- a/packages/core/installer/templates/cozystack-operator.yaml +++ b/packages/core/installer/templates/cozystack-operator.yaml @@ -1,3 +1,7 @@ +{{- $validVariants := list "talos" "generic" "hosted" -}} +{{- if not (has .Values.cozystackOperator.variant $validVariants) -}} +{{- fail (printf "Invalid cozystackOperator.variant %q: must be one of talos, generic, hosted" .Values.cozystackOperator.variant) -}} +{{- end -}} --- apiVersion: v1 kind: Namespace @@ -6,6 +10,8 @@ metadata: labels: cozystack.io/system: "true" pod-security.kubernetes.io/enforce: privileged + annotations: + helm.sh/resource-policy: keep --- apiVersion: v1 kind: ServiceAccount @@ -53,10 +59,9 @@ spec: args: - --leader-elect=true - --install-flux=true - # CRDs are also in crds/ for initial helm install, but Helm never updates - # them on upgrade and never deletes them on uninstall. The operator applies - # embedded CRDs via server-side apply on every startup, ensuring they stay - # up to date. To fully remove CRDs, delete them manually after helm uninstall. + # The operator applies embedded CRDs via server-side apply on every + # startup, ensuring they stay up to date. + # To fully remove CRDs, delete them manually after helm uninstall. - --install-crds=true - --metrics-bind-address=0 - --health-probe-bind-address=0 diff --git a/packages/core/installer/templates/packagesource.yaml b/packages/core/installer/templates/packagesource.yaml deleted file mode 100644 index 65b1e4bf..00000000 --- a/packages/core/installer/templates/packagesource.yaml +++ /dev/null @@ -1,57 +0,0 @@ -{{- $validVariants := list "talos" "generic" "hosted" -}} -{{- if not (has .Values.cozystackOperator.variant $validVariants) -}} -{{- fail (printf "Invalid cozystackOperator.variant %q: must be one of talos, generic, hosted" .Values.cozystackOperator.variant) -}} -{{- end -}} ---- -apiVersion: cozystack.io/v1alpha1 -kind: PackageSource -metadata: - name: cozystack.cozystack-platform - annotations: - operator.cozystack.io/skip-cozystack-values: "true" -spec: - sourceRef: - kind: OCIRepository - name: cozystack-platform - namespace: cozy-system - path: / - variants: - - name: default - components: - - install: - namespace: cozy-system - releaseName: cozystack-platform - name: platform - path: core/platform - valuesFiles: - - values.yaml - - name: isp-full - components: - - install: - namespace: cozy-system - releaseName: cozystack-platform - name: platform - path: core/platform - valuesFiles: - - values.yaml - - values-isp-full.yaml - - name: isp-hosted - components: - - install: - namespace: cozy-system - releaseName: cozystack-platform - name: platform - path: core/platform - valuesFiles: - - values.yaml - - values-isp-hosted.yaml - - name: isp-full-generic - components: - - install: - namespace: cozy-system - releaseName: cozystack-platform - name: platform - path: core/platform - valuesFiles: - - values.yaml - - values-isp-full-generic.yaml diff --git a/packages/core/installer/values.yaml b/packages/core/installer/values.yaml index 68894b3e..eef691ea 100644 --- a/packages/core/installer/values.yaml +++ b/packages/core/installer/values.yaml @@ -1,9 +1,9 @@ cozystackOperator: # Deployment variant: talos, generic, hosted variant: talos - image: ghcr.io/cozystack/cozystack/cozystack-operator:v1.0.0-beta.6@sha256:c7490da9c1ccb51bff4dd5657ca6a33a29ac71ad9861dfa8c72fdfc8b5765b93 + image: ghcr.io/cozystack/cozystack/cozystack-operator:v1.3.0@sha256:62574f12486bb40c901cf5ed484cca264405ce5810196d86555cbb27cce1ba48 platformSourceUrl: 'oci://ghcr.io/cozystack/cozystack/cozystack-packages' - platformSourceRef: 'digest=sha256:b29b87d1a2b80452ffd4db7516a102c30c55121552dcdb237055d4124d12c55d' + platformSourceRef: 'digest=sha256:a0b9ef938446b3132d3d22ad2262beb1027c48c9037b6c2346fdc2f19acd3036' # Generic variant configuration (only used when cozystackOperator.variant=generic) cozystack: # Kubernetes API server host (IP only, no protocol/port) diff --git a/packages/core/platform/images/migrations/migrations/26 b/packages/core/platform/images/migrations/migrations/26 index 5f385b6e..96fb9851 100755 --- a/packages/core/platform/images/migrations/migrations/26 +++ b/packages/core/platform/images/migrations/migrations/26 @@ -2,6 +2,7 @@ # Migration 26 --> 27 # Migrate monitoring resources from extra/monitoring to system/monitoring # This migration re-labels resources so they become owned by monitoring-system HelmRelease +# and deletes old helm release secrets so that helm does not diff old vs new chart manifests. set -euo pipefail @@ -35,10 +36,39 @@ relabel_resources() { done } +# Delete all helm release secrets for a given release name in a namespace. +# Uses both label selector and name-pattern matching to ensure complete cleanup. +delete_helm_secrets() { + local ns="$1" + local release="$2" + + # Primary: delete by label selector + kubectl delete secrets -n "$ns" -l "name=${release},owner=helm" --ignore-not-found + + # Fallback: find and delete by name pattern (in case labels were modified) + local remaining + remaining=$(kubectl get secrets -n "$ns" -o name | { grep "^secret/sh\.helm\.release\.v1\.${release}\." || true; }) + if [ -n "$remaining" ]; then + echo " Found secrets not matched by label selector, deleting by name..." + echo "$remaining" | while IFS= read -r secret; do + echo " Deleting $secret" + kubectl delete -n "$ns" "$secret" --ignore-not-found + done + fi + + # Verify all secrets are gone + remaining=$(kubectl get secrets -n "$ns" -o name | { grep "^secret/sh\.helm\.release\.v1\.${release}\." || true; }) + if [ -n "$remaining" ]; then + echo " ERROR: Failed to delete helm release secrets:" + echo "$remaining" + return 1 + fi +} + # Find all tenant namespaces with monitoring HelmRelease echo "Finding tenant namespaces with monitoring HelmRelease..." -NAMESPACES=$(kubectl get hr --all-namespaces -l apps.cozystack.io/application.kind=Monitoring \ - -o jsonpath='{range .items[*]}{.metadata.namespace}{"\n"}{end}' 2>/dev/null | sort -u || true) +NAMESPACES=$(kubectl get hr --all-namespaces -l cozystack.io/ui=true --field-selector=metadata.name=monitoring \ + -o jsonpath='{range .items[*]}{.metadata.namespace}{"\n"}{end}' | sort -u) if [ -z "$NAMESPACES" ]; then echo "No monitoring HelmReleases found in tenant namespaces, skipping migration" @@ -66,7 +96,7 @@ for ns in $NAMESPACES; do # Step 1: Suspend the HelmRelease echo "" echo "Step 1: Suspending HelmRelease monitoring..." - kubectl patch hr -n "$ns" monitoring --type=merge -p '{"spec":{"suspend":true}}' 2>/dev/null || true + kubectl patch hr -n "$ns" monitoring --type=merge -p '{"spec":{"suspend":true}}' # Wait a moment for reconciliation to stop sleep 2 @@ -74,7 +104,7 @@ for ns in $NAMESPACES; do # Step 2: Delete helm secrets for the monitoring release echo "" echo "Step 2: Deleting helm secrets for monitoring release..." - kubectl delete secrets -n "$ns" -l name=monitoring,owner=helm --ignore-not-found + delete_helm_secrets "$ns" "monitoring" # Step 3: Relabel resources to be owned by monitoring-system echo "" @@ -121,7 +151,9 @@ for ns in $NAMESPACES; do echo "Processing Cozystack resources..." relabel_resources "$ns" "workloadmonitors.cozystack.io" - # Step 4: Delete the suspended HelmRelease (Flux won't delete resources when HR is suspended) + # Step 4: Delete the suspended HelmRelease + # Helm secrets are already gone, so flux finalizer will find no release to uninstall + # and will simply remove the finalizer without deleting any resources. echo "" echo "Step 4: Deleting suspended HelmRelease monitoring..." kubectl delete hr -n "$ns" monitoring --ignore-not-found diff --git a/packages/core/platform/images/migrations/migrations/27 b/packages/core/platform/images/migrations/migrations/27 index 3e8719e9..5708ebbb 100755 --- a/packages/core/platform/images/migrations/migrations/27 +++ b/packages/core/platform/images/migrations/migrations/27 @@ -5,10 +5,24 @@ set -euo pipefail # Migrate Piraeus CRDs to piraeus-operator-crds Helm release for crd in linstorclusters.piraeus.io linstornodeconnections.piraeus.io linstorsatelliteconfigurations.piraeus.io linstorsatellites.piraeus.io; do - kubectl annotate crd "$crd" meta.helm.sh/release-namespace=cozy-linstor meta.helm.sh/release-name=piraeus-operator-crds --overwrite - kubectl label crd "$crd" app.kubernetes.io/managed-by=Helm helm.toolkit.fluxcd.io/namespace=cozy-linstor helm.toolkit.fluxcd.io/name=piraeus-operator-crds --overwrite + if kubectl get crd "$crd" >/dev/null 2>&1; then + echo " Relabeling CRD $crd" + kubectl annotate crd "$crd" meta.helm.sh/release-namespace=cozy-linstor meta.helm.sh/release-name=piraeus-operator-crds --overwrite + kubectl label crd "$crd" app.kubernetes.io/managed-by=Helm helm.toolkit.fluxcd.io/namespace=cozy-linstor helm.toolkit.fluxcd.io/name=piraeus-operator-crds --overwrite + else + echo " CRD $crd not found, skipping" + fi done + +# Delete old piraeus-operator helm secrets (by label and by name pattern) kubectl delete secret -n cozy-linstor -l name=piraeus-operator,owner=helm --ignore-not-found +remaining=$(kubectl get secrets -n cozy-linstor -o name 2>/dev/null | { grep "^secret/sh\.helm\.release\.v1\.piraeus-operator\." || true; }) +if [ -n "$remaining" ]; then + echo " Deleting remaining piraeus-operator helm secrets by name..." + echo "$remaining" | while IFS= read -r secret; do + kubectl delete -n cozy-linstor "$secret" --ignore-not-found + done +fi # Stamp version kubectl create configmap -n cozy-system cozystack-version \ diff --git a/packages/core/platform/images/migrations/migrations/28 b/packages/core/platform/images/migrations/migrations/28 index 259d5fd8..5174c9b0 100755 --- a/packages/core/platform/images/migrations/migrations/28 +++ b/packages/core/platform/images/migrations/migrations/28 @@ -348,7 +348,7 @@ PVCEOF # --- 3g: Clone Secrets --- echo " --- Clone Secrets ---" for secret in $(kubectl -n "$NAMESPACE" get secret -o name 2>/dev/null \ - | grep "secret/${OLD_NAME}" | grep -v "sh.helm.release"); do + | { grep "secret/${OLD_NAME}" || true; } | { grep -v "sh.helm.release" || true; }); do old_secret_name="${secret#secret/}" new_secret_name="${NEW_NAME}${old_secret_name#${OLD_NAME}}" clone_resource "$NAMESPACE" "secret" "$old_secret_name" "$new_secret_name" "$OLD_NAME" "$NEW_NAME" @@ -357,7 +357,7 @@ PVCEOF # --- 3h: Clone ConfigMaps --- echo " --- Clone ConfigMaps ---" for cm in $(kubectl -n "$NAMESPACE" get configmap -o name 2>/dev/null \ - | grep "configmap/${OLD_NAME}"); do + | { grep "configmap/${OLD_NAME}" || true; }); do old_cm_name="${cm#configmap/}" new_cm_name="${NEW_NAME}${old_cm_name#${OLD_NAME}}" clone_resource "$NAMESPACE" "configmap" "$old_cm_name" "$new_cm_name" "$OLD_NAME" "$NEW_NAME" @@ -468,13 +468,13 @@ PVCEOF fi for secret in $(kubectl -n "$NAMESPACE" get secret -o name 2>/dev/null \ - | grep "secret/${OLD_NAME}" | grep -v "sh.helm.release"); do + | { grep "secret/${OLD_NAME}" || true; } | { grep -v "sh.helm.release" || true; }); do old_secret_name="${secret#secret/}" delete_resource "$NAMESPACE" "secret" "$old_secret_name" done for cm in $(kubectl -n "$NAMESPACE" get configmap -o name 2>/dev/null \ - | grep "configmap/${OLD_NAME}"); do + | { grep "configmap/${OLD_NAME}" || true; }); do old_cm_name="${cm#configmap/}" delete_resource "$NAMESPACE" "configmap" "$old_cm_name" done @@ -611,6 +611,19 @@ done echo "" echo "=== Migration complete (${#INSTANCES[@]} instance(s)) ===" +# ============================================================ +# STEP 8: Clean up orphaned mysql-rd system HelmRelease +# ============================================================ +echo "" +echo "--- Step 8: Clean up orphaned mysql-rd HelmRelease ---" +if kubectl -n cozy-system get hr mysql-rd --no-headers 2>/dev/null | grep -q .; then + echo " [DELETE] hr/mysql-rd" + kubectl -n cozy-system delete hr mysql-rd --wait=false +else + echo " [SKIP] hr/mysql-rd already gone" +fi +kubectl -n cozy-system delete secret -l "owner=helm,name=mysql-rd" --ignore-not-found + # Stamp version kubectl create configmap -n cozy-system cozystack-version \ --from-literal=version=29 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/29 b/packages/core/platform/images/migrations/migrations/29 index a76071a5..b72c2a93 100755 --- a/packages/core/platform/images/migrations/migrations/29 +++ b/packages/core/platform/images/migrations/migrations/29 @@ -9,8 +9,6 @@ set -euo pipefail OLD_PREFIX="virtual-machine" NEW_DISK_PREFIX="vm-disk" NEW_INSTANCE_PREFIX="vm-instance" -PROTECTION_WEBHOOK_NAME="protection-webhook" -PROTECTION_WEBHOOK_NS="protection-webhook" CDI_APISERVER_NS="cozy-kubevirt-cdi" CDI_APISERVER_DEPLOY="cdi-apiserver" CDI_VALIDATING_WEBHOOKS="cdi-api-datavolume-validate cdi-api-dataimportcron-validate cdi-api-populator-validate cdi-api-validate" @@ -88,7 +86,6 @@ echo " Total: ${#INSTANCES[@]} instance(s)" # STEP 2: Migrate each instance # ============================================================ ALL_PV_NAMES=() -ALL_PROTECTED_RESOURCES=() for entry in "${INSTANCES[@]}"; do NAMESPACE="${entry%%/*}" @@ -315,7 +312,7 @@ PVCEOF # --- 2i: Clone Secrets --- echo " --- Clone Secrets ---" kubectl -n "$NAMESPACE" get secret -o name 2>/dev/null \ - | grep "secret/${OLD_NAME}" | grep -v "sh.helm.release" | grep -v "values" \ + | { grep "secret/${OLD_NAME}" || true; } | { grep -v "sh.helm.release" || true; } | { grep -v "values" || true; } \ | while IFS= read -r secret; do old_secret_name="${secret#secret/}" suffix="${old_secret_name#${OLD_NAME}}" @@ -542,7 +539,7 @@ SVCEOF # --- 2q: Delete old resources --- echo " --- Delete old resources ---" kubectl -n "$NAMESPACE" get secret -o name 2>/dev/null \ - | grep "secret/${OLD_NAME}" | grep -v "sh.helm.release" | grep -v "values" \ + | { grep "secret/${OLD_NAME}" || true; } | { grep -v "sh.helm.release" || true; } | { grep -v "values" || true; } \ | while IFS= read -r secret; do old_secret_name="${secret#secret/}" delete_resource "$NAMESPACE" "secret" "$old_secret_name" @@ -564,71 +561,17 @@ SVCEOF delete_resource "$NAMESPACE" "secret" "$VALUES_SECRET" fi - # Collect protected resources for batch deletion + # Delete old service (if exists) if resource_exists "$NAMESPACE" "svc" "$OLD_NAME"; then - ALL_PROTECTED_RESOURCES+=("${NAMESPACE}:svc/${OLD_NAME}") + delete_resource "$NAMESPACE" "svc" "$OLD_NAME" fi done # ============================================================ -# STEP 3: Delete protected resources (Services) +# STEP 3: Restore PV reclaim policies # ============================================================ echo "" -echo "--- Step 3: Delete protected resources ---" - -if [ ${#ALL_PROTECTED_RESOURCES[@]} -gt 0 ]; then - WEBHOOK_EXISTS=false - if kubectl -n "$PROTECTION_WEBHOOK_NS" get deploy "$PROTECTION_WEBHOOK_NAME" --no-headers 2>/dev/null | grep -q .; then - WEBHOOK_EXISTS=true - fi - - if [ "$WEBHOOK_EXISTS" = "true" ]; then - echo " --- Temporarily disabling protection-webhook ---" - - WEBHOOK_REPLICAS=$(kubectl -n "$PROTECTION_WEBHOOK_NS" get deploy "$PROTECTION_WEBHOOK_NAME" \ - -o jsonpath='{.spec.replicas}' 2>/dev/null || echo "1") - - echo " [SCALE] ${PROTECTION_WEBHOOK_NAME} -> 0 (was ${WEBHOOK_REPLICAS})" - kubectl -n "$PROTECTION_WEBHOOK_NS" scale deploy "$PROTECTION_WEBHOOK_NAME" --replicas=0 - - echo " [PATCH] Set failurePolicy=Ignore on ValidatingWebhookConfiguration/${PROTECTION_WEBHOOK_NAME}" - kubectl get validatingwebhookconfiguration "$PROTECTION_WEBHOOK_NAME" -o json | \ - jq '.webhooks[].failurePolicy = "Ignore"' | \ - kubectl apply -f - 2>/dev/null || true - - echo " Waiting for webhook pods to terminate..." - kubectl -n "$PROTECTION_WEBHOOK_NS" wait --for=delete pod \ - -l app.kubernetes.io/name=protection-webhook --timeout=60s 2>/dev/null || true - sleep 3 - fi - - for entry in "${ALL_PROTECTED_RESOURCES[@]}"; do - ns="${entry%%:*}" - res="${entry#*:}" - echo " [DELETE] ${ns}/${res}" - kubectl -n "$ns" delete "$res" --wait=false 2>/dev/null || true - done - - if [ "$WEBHOOK_EXISTS" = "true" ]; then - echo " [PATCH] Set failurePolicy=Fail on ValidatingWebhookConfiguration/${PROTECTION_WEBHOOK_NAME}" - kubectl get validatingwebhookconfiguration "$PROTECTION_WEBHOOK_NAME" -o json | \ - jq '.webhooks[].failurePolicy = "Fail"' | \ - kubectl apply -f - 2>/dev/null || true - - echo " [SCALE] ${PROTECTION_WEBHOOK_NAME} -> ${WEBHOOK_REPLICAS}" - kubectl -n "$PROTECTION_WEBHOOK_NS" scale deploy "$PROTECTION_WEBHOOK_NAME" \ - --replicas="$WEBHOOK_REPLICAS" - echo " --- protection-webhook restored ---" - fi -else - echo " [SKIP] No protected resources to delete" -fi - -# ============================================================ -# STEP 4: Restore PV reclaim policies -# ============================================================ -echo "" -echo "--- Step 4: Restore PV reclaim policies ---" +echo "--- Step 3: Restore PV reclaim policies ---" for pv_name in "${ALL_PV_NAMES[@]}"; do if [ -n "$pv_name" ]; then current_policy=$(kubectl get pv "$pv_name" \ @@ -643,7 +586,7 @@ for pv_name in "${ALL_PV_NAMES[@]}"; do done # ============================================================ -# STEP 5: Temporarily disable CDI datavolume webhooks +# STEP 4: Temporarily disable CDI datavolume webhooks # ============================================================ # CDI's datavolume-validate webhook rejects DataVolume creation when a PVC # with the same name already exists. We must disable it so that vm-disk @@ -652,7 +595,7 @@ done # cdi-apiserver (which serves the webhooks), then delete webhook configs. # Both are restored after vm-disk HRs reconcile. echo "" -echo "--- Step 5: Temporarily disable CDI webhooks ---" +echo "--- Step 4: Temporarily disable CDI webhooks ---" CDI_OPERATOR_REPLICAS=$(kubectl -n "$CDI_APISERVER_NS" get deploy cdi-operator \ -o jsonpath='{.spec.replicas}' 2>/dev/null || echo "1") @@ -685,10 +628,10 @@ done sleep 2 # ============================================================ -# STEP 6: Unsuspend vm-disk HelmReleases first +# STEP 5: Unsuspend vm-disk HelmReleases first # ============================================================ echo "" -echo "--- Step 6: Unsuspend vm-disk HelmReleases ---" +echo "--- Step 5: Unsuspend vm-disk HelmReleases ---" for entry in "${INSTANCES[@]}"; do ns="${entry%%/*}" instance="${entry#*/}" @@ -705,7 +648,7 @@ for entry in "${INSTANCES[@]}"; do # Force immediate reconciliation echo " [TRIGGER] Reconcile ${ns}/hr/${disk_name}" kubectl -n "$ns" annotate hr "$disk_name" --overwrite \ - "reconcile.fluxcd.io/requestedAt=$(date +%s)" 2>/dev/null || true + "reconcile.fluxcd.io/requestedAt=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" 2>/dev/null || true fi done @@ -729,12 +672,12 @@ for entry in "${INSTANCES[@]}"; do done # ============================================================ -# STEP 7: Restore CDI webhooks +# STEP 6: Restore CDI webhooks # ============================================================ # Scale cdi-operator and cdi-apiserver back up. # cdi-apiserver will recreate webhook configurations automatically on start. echo "" -echo "--- Step 7: Restore CDI webhooks ---" +echo "--- Step 6: Restore CDI webhooks ---" echo " [SCALE] cdi-operator -> ${CDI_OPERATOR_REPLICAS}" kubectl -n "$CDI_APISERVER_NS" scale deploy cdi-operator \ @@ -749,10 +692,10 @@ kubectl -n "$CDI_APISERVER_NS" rollout status deploy "$CDI_APISERVER_DEPLOY" --t echo " --- CDI webhooks restored ---" # ============================================================ -# STEP 8: Unsuspend vm-instance HelmReleases +# STEP 7: Unsuspend vm-instance HelmReleases # ============================================================ echo "" -echo "--- Step 8: Unsuspend vm-instance HelmReleases ---" +echo "--- Step 7: Unsuspend vm-instance HelmReleases ---" for entry in "${INSTANCES[@]}"; do ns="${entry%%/*}" instance="${entry#*/}" @@ -772,6 +715,19 @@ done echo "" echo "=== Migration complete (${#INSTANCES[@]} instance(s)) ===" +# ============================================================ +# STEP 8: Clean up orphaned virtual-machine-rd system HelmRelease +# ============================================================ +echo "" +echo "--- Step 8: Clean up orphaned virtual-machine-rd HelmRelease ---" +if kubectl -n cozy-system get hr virtual-machine-rd --no-headers 2>/dev/null | grep -q .; then + echo " [DELETE] hr/virtual-machine-rd" + kubectl -n cozy-system delete hr virtual-machine-rd --wait=false +else + echo " [SKIP] hr/virtual-machine-rd already gone" +fi +kubectl -n cozy-system delete secret -l "owner=helm,name=virtual-machine-rd" --ignore-not-found + # Stamp version kubectl create configmap -n cozy-system cozystack-version \ --from-literal=version=30 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/32 b/packages/core/platform/images/migrations/migrations/32 new file mode 100755 index 00000000..c93a9312 --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/32 @@ -0,0 +1,92 @@ +#!/bin/sh +# Migration 32 --> 33 +# Convert publishing.certificates.issuerType to solver + issuerName in +# the cozystack-platform Package resource. +# +# Old field (pre-refactor schema): +# publishing.certificates.issuerType: "http01" | "cloudflare" +# New fields: +# publishing.certificates.solver: "http01" | "dns01" +# publishing.certificates.issuerName: "letsencrypt-prod" (or custom) +# +# Conversion table: +# cloudflare -> solver: dns01, issuerName: letsencrypt-prod +# http01 -> solver: http01, issuerName: letsencrypt-prod +# -> issuerName: (solver left at chart default) +# -> no-op + +set -euo pipefail + +PACKAGE_NAME="cozystack.cozystack-platform" + +# Check if Package exists +if ! kubectl get package "$PACKAGE_NAME" >/dev/null 2>&1; then + echo "Package $PACKAGE_NAME not found, skipping migration" + kubectl create configmap -n cozy-system cozystack-version \ + --from-literal=version=33 --dry-run=client -o yaml | kubectl apply -f - + exit 0 +fi + +# Read current issuerType value +ISSUER_TYPE=$(kubectl get package "$PACKAGE_NAME" -o json | \ + jq -r '.spec.components.platform.values.publishing.certificates.issuerType // ""') + +if [ -z "$ISSUER_TYPE" ]; then + echo "No issuerType found in Package $PACKAGE_NAME, nothing to migrate" + kubectl create configmap -n cozy-system cozystack-version \ + --from-literal=version=33 --dry-run=client -o yaml | kubectl apply -f - + exit 0 +fi + +echo "Found issuerType: $ISSUER_TYPE" + +# Convert old issuerType to new solver/issuerName +SOLVER="" +ISSUER_NAME="" +case "$ISSUER_TYPE" in + cloudflare) + SOLVER="dns01" + ISSUER_NAME="letsencrypt-prod" + ;; + http01) + SOLVER="http01" + ISSUER_NAME="letsencrypt-prod" + ;; + *) + # Unrecognised value — treat as custom ClusterIssuer name, no solver override + ISSUER_NAME="$ISSUER_TYPE" + ;; +esac + +echo "Converting to: solver=${SOLVER:-}, issuerName=${ISSUER_NAME:-}" + +# Build the certificates patch: +# - null removes issuerType (JSON merge patch semantics) +# - solver and issuerName are included only when non-empty +CERTS_PATCH=$(jq -n --arg solver "$SOLVER" --arg issuerName "$ISSUER_NAME" ' + {"issuerType": null} + + (if $solver != "" then {"solver": $solver} else {} end) + + (if $issuerName != "" then {"issuerName": $issuerName} else {} end) +') + +PATCH_JSON=$(jq -n --argjson certs "$CERTS_PATCH" '{ + "spec": { + "components": { + "platform": { + "values": { + "publishing": { + "certificates": $certs + } + } + } + } + } +}') + +kubectl patch package "$PACKAGE_NAME" --type=merge --patch "$PATCH_JSON" + +echo "Migration complete: issuerType=$ISSUER_TYPE -> solver=${SOLVER:-} issuerName=${ISSUER_NAME:-}" + +# Stamp version +kubectl create configmap -n cozy-system cozystack-version \ + --from-literal=version=33 --dry-run=client -o yaml | kubectl apply -f - diff --git a/packages/core/platform/images/migrations/migrations/33 b/packages/core/platform/images/migrations/migrations/33 new file mode 100755 index 00000000..5da6788f --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/33 @@ -0,0 +1,30 @@ +#!/bin/sh +# Migration 33 --> 34 +# Clean up orphaned system -rd HelmReleases left after application renames. +# +# These HelmReleases reference ExternalArtifacts that no longer exist: +# ferretdb-rd -> replaced by mongodb-rd +# mysql-rd -> replaced by mariadb-rd (migration 28 handled user HRs only) +# virtual-machine-rd -> replaced by vm-disk-rd + vm-instance-rd (migration 29 handled user HRs only) +# +# Idempotent: safe to re-run. + +set -euo pipefail + +echo "=== Cleaning up orphaned -rd HelmReleases ===" + +for hr_name in ferretdb-rd mysql-rd virtual-machine-rd; do + if kubectl -n cozy-system get hr "$hr_name" --no-headers 2>/dev/null | grep -q .; then + echo " [DELETE] hr/${hr_name}" + kubectl -n cozy-system delete hr "$hr_name" --wait=false + else + echo " [SKIP] hr/${hr_name} already gone" + fi + kubectl -n cozy-system delete secret -l "owner=helm,name=${hr_name}" --ignore-not-found +done + +echo "=== Cleanup complete ===" + +# Stamp version +kubectl create configmap -n cozy-system cozystack-version \ + --from-literal=version=34 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/34 b/packages/core/platform/images/migrations/migrations/34 new file mode 100755 index 00000000..f031d590 --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/34 @@ -0,0 +1,46 @@ +#!/bin/sh +# Migration 34 --> 35 +# Backfill spec.version on rabbitmq.apps.cozystack.io resources. +# +# Before this migration RabbitMQ had no user-selectable version; the +# operator always used its built-in default image (v3.x). A version field +# was added in this release. Without this migration every existing cluster +# would be upgraded to the new default (v4.2) on the next reconcile. +# +# Set spec.version to "v3.13" for any rabbitmq app resource that does not +# already have it set. + +set -euo pipefail + +DEFAULT_VERSION="v3.13" + +# Skip if the CRD does not exist (rabbitmq was never installed) +if ! kubectl api-resources --api-group=apps.cozystack.io -o name 2>/dev/null | grep -q '^rabbitmqs\.'; then + echo "CRD rabbitmqs.apps.cozystack.io not found, skipping migration" + kubectl create configmap -n cozy-system cozystack-version \ + --from-literal=version=35 --dry-run=client -o yaml | kubectl apply -f- + exit 0 +fi + +RABBITMQS=$(kubectl get rabbitmqs.apps.cozystack.io -A -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}{"\n"}{end}') +for resource in $RABBITMQS; do + NS="${resource%%/*}" + APP_NAME="${resource##*/}" + + # Skip if spec.version is already set + CURRENT_VER=$(kubectl get rabbitmqs.apps.cozystack.io -n "$NS" "$APP_NAME" \ + -o jsonpath='{.spec.version}') + if [ -n "$CURRENT_VER" ]; then + echo "SKIP $NS/$APP_NAME: spec.version already set to '$CURRENT_VER'" + continue + fi + + echo "Patching rabbitmq/$APP_NAME in $NS: setting version=$DEFAULT_VERSION" + + kubectl patch rabbitmqs.apps.cozystack.io -n "$NS" "$APP_NAME" --type=merge \ + --patch "{\"spec\":{\"version\":\"${DEFAULT_VERSION}\"}}" +done + +# Stamp version +kubectl create configmap -n cozy-system cozystack-version \ + --from-literal=version=35 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/35 b/packages/core/platform/images/migrations/migrations/35 new file mode 100755 index 00000000..a7471180 --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/35 @@ -0,0 +1,21 @@ +#!/bin/sh +# Migration 35 --> 36 +# Add helm.sh/resource-policy=keep annotation to existing VLogs resources +# so they are preserved when the monitoring helm release upgrades to VLCluster. +# Users will need to manually verify the new cluster is working, then optionally +# migrate historical data and delete old VLogs resources. + +set -euo pipefail + +VLOGS=$(kubectl get vlogs.operator.victoriametrics.com --all-namespaces --output jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}{"\n"}{end}' 2>/dev/null || true) +for resource in $VLOGS; do + NS="${resource%%/*}" + NAME="${resource##*/}" + echo "Adding keep annotation to VLogs/$NAME in $NS" + kubectl annotate vlogs.operator.victoriametrics.com --namespace "$NS" "$NAME" \ + helm.sh/resource-policy=keep \ + --overwrite +done + +kubectl create configmap --namespace cozy-system cozystack-version \ + --from-literal=version=36 --dry-run=client --output yaml | kubectl apply --filename - diff --git a/packages/core/platform/images/migrations/migrations/36 b/packages/core/platform/images/migrations/migrations/36 new file mode 100755 index 00000000..0f0a94dd --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/36 @@ -0,0 +1,22 @@ +#!/bin/sh +# Migration 36 --> 37 +# +# Copy spec.subnets to spec.networks in VMInstance resources. +# The old "subnets" field is kept intact because migrations run before +# the new CRD is applied; removing it now would lose data. + +set -e + +if ! kubectl get crd vminstances.apps.cozystack.io >/dev/null 2>&1; then + echo "VMInstance CRD not found, skipping migration" +else +kubectl get vminstances.apps.cozystack.io -A -o json | jq -r ' + .items[] + | select(.spec.subnets != null and (.spec.subnets | length) > 0) + | select(.spec.networks == null or (.spec.networks | length) == 0) + | "kubectl -n \(.metadata.namespace) patch vminstances.apps.cozystack.io \(.metadata.name) --type=json -p '\''[{\"op\":\"add\",\"path\":\"/spec/networks\",\"value\":\(.spec.subnets|tojson)}]'\''" +' | sh -ex +fi + +# Write version to cozystack-version config +kubectl create configmap -n cozy-system cozystack-version --from-literal=version=37 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/37 b/packages/core/platform/images/migrations/migrations/37 new file mode 100755 index 00000000..856da262 --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/37 @@ -0,0 +1,78 @@ +#!/bin/bash +# Migration 37 --> 38 +# Pin PostgreSQL image to 17.7-standard-trixie for system databases. +# +# This migration updates the imageName for all CNPG clusters belonging to +# system components (keycloak-db, grafana-db, alerta-db, seaweedfs-db, and +# harbor's dynamically-named DB): +# - If imageName is not set: pins to 17.7-standard-trixie +# - If imageName has any PG 17 tag: forces 17.7-standard-trixie +# - If imageName has a bare version tag (e.g. :18.1): appends -standard-trixie +# +# NOTE: This migration ONLY updates system CNPG Cluster resources (clusters.postgresql.cnpg.io), +# NOT user-created Postgres applications. + +set -euo pipefail + +TARGET_IMAGE="ghcr.io/cloudnative-pg/postgresql:17.7-standard-trixie" + +# Static system database names +STATIC_DB_NAMES="keycloak-db grafana-db alerta-db seaweedfs-db" + +echo "=== Updating PostgreSQL images for system databases ===" +echo "Target image: $TARGET_IMAGE" + +# Fetch all CNPG clusters with their name, namespace, imageName, and Helm release annotation in one call +ALL_CLUSTERS=$(kubectl get clusters.postgresql.cnpg.io -A \ + -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.spec.imageName}{"\t"}{.metadata.annotations.meta\.helm\.sh/release-name}{"\n"}{end}' 2>/dev/null || true) + +while IFS=$'\t' read -r NAMESPACE CLUSTER_NAME CURRENT_IMAGE HELM_RELEASE; do + [ -z "$NAMESPACE" ] && continue + + # Check if cluster name matches one of the static system databases + MATCH=false + for db_name in $STATIC_DB_NAMES; do + if [ "$CLUSTER_NAME" = "$db_name" ]; then + MATCH=true + break + fi + done + + # For harbor: match clusters ending with -db whose Helm release ends with -system + if [ "$MATCH" = false ] && [[ "$CLUSTER_NAME" == *-db ]] && [[ "$HELM_RELEASE" == *-system ]]; then + MATCH=true + fi + + if [ "$MATCH" = false ]; then + continue + fi + + PATCH_IMAGE="" + + if [ -z "$CURRENT_IMAGE" ]; then + # imageName not set — pin to target + PATCH_IMAGE="$TARGET_IMAGE" + echo "PATCH $NAMESPACE/$CLUSTER_NAME: imageName not set, setting to $PATCH_IMAGE" + elif [[ "$CURRENT_IMAGE" =~ :17\. ]]; then + # Any PG 17 image — force to the pinned 17.7-standard-trixie + PATCH_IMAGE="$TARGET_IMAGE" + echo "PATCH $NAMESPACE/$CLUSTER_NAME: PG 17 detected, $CURRENT_IMAGE -> $PATCH_IMAGE" + elif [[ "$CURRENT_IMAGE" =~ :[0-9]+\.[0-9]+$ ]]; then + # Bare version tag for other majors (e.g. :18.1) — append -standard-trixie suffix + PATCH_IMAGE="${CURRENT_IMAGE}-standard-trixie" + echo "PATCH $NAMESPACE/$CLUSTER_NAME: bare tag detected, $CURRENT_IMAGE -> $PATCH_IMAGE" + else + echo "SKIP $NAMESPACE/$CLUSTER_NAME: imageName already set to $CURRENT_IMAGE" + continue + fi + + kubectl patch clusters.postgresql.cnpg.io -n "$NAMESPACE" "$CLUSTER_NAME" \ + --type=merge \ + --patch "{\"spec\":{\"imageName\":\"${PATCH_IMAGE}\"}}" +done <<< "$ALL_CLUSTERS" + +echo "=== PostgreSQL image update completed ===" + +# Stamp version +kubectl create configmap -n cozy-system cozystack-version \ + --from-literal=version=38 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/38 b/packages/core/platform/images/migrations/migrations/38 new file mode 100755 index 00000000..59536cb1 --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/38 @@ -0,0 +1,52 @@ +#!/bin/sh +# Migration 38 --> 39 +# Rename DataVolumes/PVCs in cozy-public from vm-image- to vm-default-images-. +# +# The vm-disk package previously looked up golden images with the prefix "vm-image-". +# The new vm-default-images package uses the prefix "vm-default-images-". +# This migration renames any existing DataVolumes so that live vm-disk resources +# continue to resolve their source PVC after upgrade. + +set -euo pipefail + +NAMESPACE="cozy-public" + +# Skip if CDI DataVolume CRD is not installed +if ! kubectl api-resources --api-group=cdi.kubevirt.io -o name 2>/dev/null | grep -q '^datavolumes\.'; then + echo "CDI DataVolume CRD not found, skipping rename" + kubectl create configmap -n cozy-system cozystack-version \ + --from-literal=version=39 --dry-run=client -o yaml | kubectl apply -f- + exit 0 +fi + +# Find DataVolumes whose name starts with "vm-image-" +DVNAMES=$(kubectl get datavolumes.cdi.kubevirt.io -n "$NAMESPACE" \ + -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' 2>/dev/null \ + | grep '^vm-image-' || true) + +if [ -z "$DVNAMES" ]; then + echo "No vm-image-* DataVolumes found in $NAMESPACE, nothing to rename" +else + for DVNAME in $DVNAMES; do + NEWNAME="vm-default-images-${DVNAME#vm-image-}" + echo "Renaming DataVolume $DVNAME -> $NEWNAME in namespace $NAMESPACE" + + # Export existing DV spec, swap name, and apply as new object + kubectl get datavolumes.cdi.kubevirt.io -n "$NAMESPACE" "$DVNAME" -o json \ + | jq --arg new "$NEWNAME" ' + del(.metadata.resourceVersion, .metadata.uid, .metadata.creationTimestamp, + .metadata.generation, .metadata.selfLink, .metadata.managedFields, + .status) + | .metadata.name = $new + ' \ + | kubectl apply -f - + + # Delete the old DataVolume + kubectl delete datavolumes.cdi.kubevirt.io -n "$NAMESPACE" "$DVNAME" --ignore-not-found=true + echo "Renamed $DVNAME -> $NEWNAME" + done +fi + +# Stamp version +kubectl create configmap -n cozy-system cozystack-version \ + --from-literal=version=39 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/run-migrations.sh b/packages/core/platform/images/migrations/run-migrations.sh index c35ade21..a8224ef7 100755 --- a/packages/core/platform/images/migrations/run-migrations.sh +++ b/packages/core/platform/images/migrations/run-migrations.sh @@ -24,7 +24,7 @@ if [ "$CURRENT_VERSION" -ge "$TARGET_VERSION" ]; then fi # Run migrations sequentially from current version to target version -for i in $(seq $((CURRENT_VERSION + 1)) $TARGET_VERSION); do +for i in $(seq $CURRENT_VERSION $((TARGET_VERSION - 1))); do if [ -f "/migrations/$i" ]; then echo "Running migration $i" chmod +x /migrations/$i diff --git a/packages/core/platform/sources/backupstrategy-controller.yaml b/packages/core/platform/sources/backupstrategy-controller.yaml index 3647e8dc..b86c5c0d 100644 --- a/packages/core/platform/sources/backupstrategy-controller.yaml +++ b/packages/core/platform/sources/backupstrategy-controller.yaml @@ -18,5 +18,5 @@ spec: path: system/backupstrategy-controller install: privileged: true - namespace: cozy-backupstrategy-controller + namespace: cozy-backup-controller releaseName: backupstrategy-controller diff --git a/packages/core/platform/sources/cozystack-scheduler.yaml b/packages/core/platform/sources/cozystack-scheduler.yaml new file mode 100644 index 00000000..52e85a4f --- /dev/null +++ b/packages/core/platform/sources/cozystack-scheduler.yaml @@ -0,0 +1,30 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.cozystack-scheduler +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + components: + - name: cozystack-scheduler + path: system/cozystack-scheduler + install: + namespace: kube-system + releaseName: cozystack-scheduler + - name: linstor + dependsOn: + - cozystack.linstor-scheduler + components: + - name: cozystack-scheduler + path: system/cozystack-scheduler + valuesFiles: + - values-linstor.yaml + install: + namespace: kube-system + releaseName: cozystack-scheduler diff --git a/packages/core/platform/sources/external-dns-application.yaml b/packages/core/platform/sources/external-dns-application.yaml new file mode 100644 index 00000000..0a37dc52 --- /dev/null +++ b/packages/core/platform/sources/external-dns-application.yaml @@ -0,0 +1,29 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.external-dns-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: external-dns-system + path: system/external-dns + - name: external-dns + path: extra/external-dns + libraries: ["cozy-lib"] + - name: external-dns-rd + path: system/external-dns-rd + install: + namespace: cozy-system + releaseName: external-dns-rd diff --git a/packages/core/platform/sources/hami.yaml b/packages/core/platform/sources/hami.yaml new file mode 100644 index 00000000..0184e405 --- /dev/null +++ b/packages/core/platform/sources/hami.yaml @@ -0,0 +1,24 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.hami +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.gpu-operator + components: + - name: hami + path: system/hami + valuesFiles: + - values.yaml + install: + privileged: true + namespace: cozy-hami + releaseName: hami diff --git a/packages/core/platform/sources/kubernetes-application.yaml b/packages/core/platform/sources/kubernetes-application.yaml index 9787cb19..088383ad 100644 --- a/packages/core/platform/sources/kubernetes-application.yaml +++ b/packages/core/platform/sources/kubernetes-application.yaml @@ -52,6 +52,8 @@ spec: path: system/cilium - name: kubernetes-gpu-operator path: system/gpu-operator + - name: kubernetes-hami + path: system/hami - name: kubernetes-vertical-pod-autoscaler path: system/vertical-pod-autoscaler - name: kubernetes-prometheus-operator-crds diff --git a/packages/core/platform/sources/linstor-gui.yaml b/packages/core/platform/sources/linstor-gui.yaml new file mode 100644 index 00000000..005e1fb7 --- /dev/null +++ b/packages/core/platform/sources/linstor-gui.yaml @@ -0,0 +1,21 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.linstor-gui +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.linstor + components: + - name: linstor-gui + path: system/linstor-gui + install: + namespace: cozy-linstor + releaseName: linstor-gui diff --git a/packages/core/platform/sources/networking.yaml b/packages/core/platform/sources/networking.yaml index 8e8485b3..bece76f8 100644 --- a/packages/core/platform/sources/networking.yaml +++ b/packages/core/platform/sources/networking.yaml @@ -66,6 +66,7 @@ spec: path: system/cilium valuesFiles: - values.yaml + - values-apparmor.yaml install: privileged: true namespace: cozy-cilium @@ -117,6 +118,7 @@ spec: path: system/cilium valuesFiles: - values.yaml + - values-apparmor.yaml - values-kubeovn.yaml install: privileged: true diff --git a/packages/core/platform/sources/openbao-application.yaml b/packages/core/platform/sources/openbao-application.yaml new file mode 100644 index 00000000..438148af --- /dev/null +++ b/packages/core/platform/sources/openbao-application.yaml @@ -0,0 +1,29 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.openbao-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: openbao-system + path: system/openbao + - name: openbao + path: apps/openbao + libraries: ["cozy-lib"] + - name: openbao-rd + path: system/openbao-rd + install: + namespace: cozy-system + releaseName: openbao-rd diff --git a/packages/core/platform/sources/opensearch-application.yaml b/packages/core/platform/sources/opensearch-application.yaml new file mode 100644 index 00000000..19358c21 --- /dev/null +++ b/packages/core/platform/sources/opensearch-application.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.opensearch-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.opensearch-operator + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: opensearch + path: apps/opensearch + libraries: ["cozy-lib"] + - name: opensearch-rd + path: system/opensearch-rd + install: + namespace: cozy-system + releaseName: opensearch-rd diff --git a/packages/core/platform/sources/opensearch-operator.yaml b/packages/core/platform/sources/opensearch-operator.yaml new file mode 100644 index 00000000..21dd7a43 --- /dev/null +++ b/packages/core/platform/sources/opensearch-operator.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.opensearch-operator +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.prometheus-operator-crds + components: + - name: opensearch-operator + path: system/opensearch-operator + install: + namespace: cozy-opensearch-operator + releaseName: opensearch-operator diff --git a/packages/core/platform/sources/vm-default-images.yaml b/packages/core/platform/sources/vm-default-images.yaml new file mode 100644 index 00000000..16cd6a94 --- /dev/null +++ b/packages/core/platform/sources/vm-default-images.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.vm-default-images +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.kubevirt-cdi + components: + - name: vm-default-images + path: system/vm-default-images + install: + namespace: cozy-system + releaseName: vm-default-images diff --git a/packages/core/platform/templates/_helpers.tpl b/packages/core/platform/templates/_helpers.tpl index 684ee812..4bc02429 100644 --- a/packages/core/platform/templates/_helpers.tpl +++ b/packages/core/platform/templates/_helpers.tpl @@ -13,6 +13,8 @@ apiVersion: cozystack.io/v1alpha1 kind: Package metadata: name: {{ $name }} + annotations: + helm.sh/resource-policy: keep spec: variant: {{ $variant }} {{- if $components }} @@ -40,6 +42,8 @@ apiVersion: cozystack.io/v1alpha1 kind: Package metadata: name: {{ $name }} + annotations: + helm.sh/resource-policy: keep spec: variant: {{ $variant }} {{- end }} diff --git a/packages/core/platform/templates/apps.yaml b/packages/core/platform/templates/apps.yaml index 8484fe4c..6e1ae25a 100644 --- a/packages/core/platform/templates/apps.yaml +++ b/packages/core/platform/templates/apps.yaml @@ -21,19 +21,25 @@ stringData: _cluster: root-host: {{ $rootHost | quote }} bundle-name: {{ .Values.bundles.system.variant | quote }} - clusterissuer: {{ .Values.publishing.certificates.issuerType | quote }} + solver: {{ .Values.publishing.certificates.solver | quote }} + issuer-name: {{ .Values.publishing.certificates.issuerName | quote }} oidc-enabled: {{ .Values.authentication.oidc.enabled | quote }} oidc-insecure-skip-verify: {{ .Values.authentication.oidc.insecureSkipVerify | quote }} extra-keycloak-redirect-uri-for-dashboard: {{ index .Values.authentication.oidc.keycloakExtraRedirectUri | quote }} + keycloak-internal-url: {{ .Values.authentication.oidc.keycloakInternalUrl | quote }} expose-services: {{ .Values.publishing.exposedServices | join "," | quote }} expose-ingress: {{ .Values.publishing.ingressName | quote }} expose-external-ips: {{ .Values.publishing.externalIPs | join "," | quote }} + expose-mode: {{ .Values.publishing.exposure | default "externalIPs" | quote }} cluster-domain: {{ .Values.networking.clusterDomain | quote }} api-server-endpoint: {{ .Values.publishing.apiServerEndpoint | quote }} {{- with .Values.branding }} branding: {{- . | toYaml | nindent 8 }} {{- end }} + cpu-allocation-ratio: {{ .Values.resources.cpuAllocationRatio | quote }} + memory-allocation-ratio: {{ .Values.resources.memoryAllocationRatio | quote }} + ephemeral-storage-allocation-ratio: {{ .Values.resources.ephemeralStorageAllocationRatio | quote }} {{- with .Values.scheduling }} scheduling: {{- . | toYaml | nindent 8 }} diff --git a/packages/core/platform/templates/bundles/iaas.yaml b/packages/core/platform/templates/bundles/iaas.yaml index 41dc5181..66ec98b2 100644 --- a/packages/core/platform/templates/bundles/iaas.yaml +++ b/packages/core/platform/templates/bundles/iaas.yaml @@ -2,9 +2,15 @@ {{- fail "bundles.iaas.enabled can only be true when bundles.system.variant is 'isp-full' or 'isp-full-generic'" }} {{- end }} {{- if and .Values.bundles.iaas.enabled (or (eq .Values.bundles.system.variant "isp-full") (eq .Values.bundles.system.variant "isp-full-generic")) }} -{{include "cozystack.platform.package.default" (list "cozystack.kubevirt" $) }} +{{- $kubevirtComponents := dict -}} +{{- if .Values.resources.cpuAllocationRatio -}} +{{- $_ := set $kubevirtComponents "kubevirt" (dict "values" (dict "cpuAllocationRatio" (.Values.resources.cpuAllocationRatio | int))) -}} +{{- end -}} +{{include "cozystack.platform.package" (list "cozystack.kubevirt" "default" $ $kubevirtComponents) }} {{include "cozystack.platform.package.default" (list "cozystack.kubevirt-cdi" $) }} +{{include "cozystack.platform.package.optional.default" (list "cozystack.vm-default-images" $) }} {{include "cozystack.platform.package.optional.default" (list "cozystack.gpu-operator" $) }} +{{include "cozystack.platform.package.optional.default" (list "cozystack.hami" $) }} {{include "cozystack.platform.package.default" (list "cozystack.kamaji" $) }} {{include "cozystack.platform.package.default" (list "cozystack.capi-operator" $) }} {{include "cozystack.platform.package.default" (list "cozystack.capi-provider-bootstrap-kubeadm" $) }} diff --git a/packages/core/platform/templates/bundles/paas.yaml b/packages/core/platform/templates/bundles/paas.yaml index 6a126304..54854368 100644 --- a/packages/core/platform/templates/bundles/paas.yaml +++ b/packages/core/platform/templates/bundles/paas.yaml @@ -16,6 +16,7 @@ {{include "cozystack.platform.package.default" (list "cozystack.mariadb-application" $) }} {{include "cozystack.platform.package.default" (list "cozystack.mongodb-application" $) }} {{include "cozystack.platform.package.default" (list "cozystack.nats-application" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.openbao-application" $) }} {{include "cozystack.platform.package.default" (list "cozystack.postgres-application" $) }} {{include "cozystack.platform.package.default" (list "cozystack.qdrant-application" $) }} {{include "cozystack.platform.package.default" (list "cozystack.rabbitmq-application" $) }} diff --git a/packages/core/platform/templates/bundles/system.yaml b/packages/core/platform/templates/bundles/system.yaml index c24726ef..6c587c82 100644 --- a/packages/core/platform/templates/bundles/system.yaml +++ b/packages/core/platform/templates/bundles/system.yaml @@ -25,10 +25,12 @@ {{include "cozystack.platform.package" (list "cozystack.networking" "kubeovn-cilium" $ $networkingComponents) }} {{include "cozystack.platform.system.common-packages" $ }} {{include "cozystack.platform.package.default" (list "cozystack.linstor" $) }} +{{include "cozystack.platform.package" (list "cozystack.cozystack-scheduler" "linstor" $) }} {{- end }} {{- if eq .Values.bundles.system.variant "isp-hosted" }} {{include "cozystack.platform.package" (list "cozystack.networking" "noop" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.cozystack-scheduler" $) }} {{- end }} {{- if eq .Values.bundles.system.variant "isp-full-generic" }} @@ -70,15 +72,12 @@ "SVC_CIDR" $svcCIDR "JOIN_CIDR" $joinCIDR -}} {{- $kubeovnDict := dict "ipv4" $kubeovnIpv4 -}} -{{- /* Set MASTER_NODES: explicit value > parsed from apiServerEndpoint > empty (use helm lookup) */ -}} +{{- /* Set MASTER_NODES: explicit value or empty (let kube-ovn discover nodes by label) */ -}} {{- $masterNodes := .Values.networking.kubeovn.MASTER_NODES -}} -{{- if and (not $masterNodes) $apiServerEndpoint -}} -{{- $masterNodes = $apiHost -}} -{{- end -}} {{- if $masterNodes -}} {{- $_ := set $kubeovnDict "MASTER_NODES" $masterNodes -}} {{- end -}} -{{- /* For generic k8s (k3s, kubeadm), control-plane label has value "true" */ -}} +{{- /* For generic k8s (k3s), control-plane label has value "true" */ -}} {{- $_ := set $kubeovnDict "MASTER_NODES_LABEL" "node-role.kubernetes.io/control-plane=true" -}} {{- $kubeovnValues := dict "kube-ovn" $kubeovnDict -}} {{- $_ := set $networkingComponents "kubeovn" (dict "values" $kubeovnValues) -}} @@ -95,6 +94,7 @@ {{- /* Pass talos.enabled: false to linstor for generic Linux */ -}} {{- $linstorComponents := dict "linstor" (dict "values" (dict "talos" (dict "enabled" false))) -}} {{include "cozystack.platform.package" (list "cozystack.linstor" "default" $ $linstorComponents) }} +{{include "cozystack.platform.package" (list "cozystack.cozystack-scheduler" "linstor" $) }} {{- end }} # Cozystack Engine @@ -105,9 +105,6 @@ {{- /* cozystack-api DaemonSet */ -}} {{- $apiValues := dict "cozystackAPI" (dict "nodeSelector" $genericNodeSelector) -}} {{- $_ := set $cozystackEngineComponents "cozystack-api" (dict "values" $apiValues) -}} -{{- /* lineage-controller-webhook DaemonSet */ -}} -{{- $lineageValues := dict "lineageControllerWebhook" (dict "nodeSelector" $genericNodeSelector) -}} -{{- $_ := set $cozystackEngineComponents "lineage-controller-webhook" (dict "values" $lineageValues) -}} {{- end -}} {{- if .Values.authentication.oidc.enabled }} {{include "cozystack.platform.package" (list "cozystack.cozystack-engine" "oidc" $ $cozystackEngineComponents) }} @@ -133,7 +130,6 @@ {{include "cozystack.platform.package.default" (list "cozystack.cozystack-basics" $) }} {{include "cozystack.platform.package.default" (list "cozystack.backupstrategy-controller" $) }} {{include "cozystack.platform.package.default" (list "cozystack.backup-controller" $) }} -{{include "cozystack.platform.package.optional.default" (list "cozystack.velero" $) }} {{include "cozystack.platform.package.default" (list "cozystack.vertical-pod-autoscaler" $) }} {{include "cozystack.platform.package.default" (list "cozystack.metrics-server" $) }} {{- $monitoringAgentsComponents := dict "monitoring-agents" (dict "values" (dict "global" (dict "target" "tenant-root"))) -}} @@ -149,7 +145,10 @@ {{include "cozystack.platform.package.optional.default" (list "cozystack.nfs-driver" $) }} {{include "cozystack.platform.package.optional.default" (list "cozystack.telepresence" $) }} {{include "cozystack.platform.package.optional.default" (list "cozystack.external-dns" $) }} +{{include "cozystack.platform.package.optional.default" (list "cozystack.external-dns-application" $) }} {{include "cozystack.platform.package.optional.default" (list "cozystack.external-secrets-operator" $) }} +{{include "cozystack.platform.package.optional.default" (list "cozystack.velero" $) }} +{{include "cozystack.platform.package.optional.default" (list "cozystack.linstor-gui" $) }} {{- if has "cozystack.bootbox" (default (list) .Values.bundles.enabledPackages) }} {{include "cozystack.platform.package.default" (list "cozystack.bootbox-application" $) }} {{include "cozystack.platform.package.default" (list "cozystack.bootbox" $) }} diff --git a/packages/core/platform/templates/cozystack-version.yaml b/packages/core/platform/templates/cozystack-version.yaml index 8e3ff6c6..09b845c2 100644 --- a/packages/core/platform/templates/cozystack-version.yaml +++ b/packages/core/platform/templates/cozystack-version.yaml @@ -6,6 +6,8 @@ kind: ConfigMap metadata: name: cozystack-version namespace: {{ .Release.Namespace }} + annotations: + helm.sh/resource-policy: keep data: version: {{ .Values.migrations.targetVersion | quote }} {{- end }} diff --git a/packages/core/platform/values.yaml b/packages/core/platform/values.yaml index 5caab16d..605e23bc 100644 --- a/packages/core/platform/values.yaml +++ b/packages/core/platform/values.yaml @@ -5,8 +5,8 @@ sourceRef: path: / migrations: enabled: false - image: ghcr.io/cozystack/cozystack/platform-migrations:v1.0.0-beta.6@sha256:37c78dafcedbdad94acd9912550db0b4875897150666b8a06edfa894de99064e - targetVersion: 32 + image: ghcr.io/cozystack/cozystack/platform-migrations:v1.3.0@sha256:555e4b76421361805a84bc9088b01b23a9c4a9430bd8ebd2db82ef9677d7008c + targetVersion: 39 # Bundle deployment configuration bundles: system: @@ -45,14 +45,55 @@ publishing: - cdi-uploadproxy apiServerEndpoint: "" # example: "https://api.example.org" externalIPs: [] + # Exposure mode for the ingress-nginx Service. When "externalIPs" (current + # default) is selected, the Service is created as ClusterIP with + # Service.spec.externalIPs set from publishing.externalIPs. When + # "loadBalancer" is selected, the Service is type: LoadBalancer and a + # CiliumLoadBalancerIPPool makes those same addresses allocatable via LB IPAM. + # + # Service.spec.externalIPs is deprecated upstream in Kubernetes v1.36 + # (KEP-5707). The AllowServiceExternalIPs feature gate is expected to default + # to false around v1.40 and the implementation removed around v1.43 — switch + # to "loadBalancer" before upgrading past v1.40. + # + # Caveats for the "loadBalancer" mode: + # - publishing.externalIPs must contain at least one non-empty address, + # otherwise the chart render fails with an explicit error (a LoadBalancer + # Service without a pool would sit in forever). + # - The ingress-nginx Service is created with externalTrafficPolicy: Local + # to preserve the client source IP. Traffic arriving on a node that does + # not host an ingress-nginx pod is dropped, so the external IP must be + # routed to a node that runs the ingress pod (floating IP / keepalived / + # upstream router / podAntiAffinity). + # - Cilium does NOT announce the IP on its own unless L2 announcements or + # BGP are enabled in the Cilium values (disabled by default in Cozystack). + # This mode assumes the operator already routes the externalIPs to a + # cluster node; enabling announcements is out of scope for this setting. + # - Switching this value on a running cluster causes the ingress-nginx + # Service to be recreated (the HelmRelease has upgrade.force: true and + # the Service kind changes between ClusterIP and LoadBalancer). Expect a + # brief interruption of ingress traffic during the flip. + # + # Scope: this setting only controls the ingress-nginx Service. Other + # cozystack components that currently write Service.spec.externalIPs directly + # (e.g. the vpn app at packages/apps/vpn/templates/service.yaml) are NOT + # migrated by flipping this value and must be addressed separately before + # the AllowServiceExternalIPs feature gate flips to off in ~v1.40. + exposure: externalIPs # "externalIPs" or "loadBalancer" certificates: - issuerType: http01 # "http01" or "cloudflare" + solver: http01 # "http01" or "dns01" + issuerName: letsencrypt-prod # Authentication configuration authentication: oidc: enabled: false insecureSkipVerify: false keycloakExtraRedirectUri: "" + # Internal URL to access KeyCloak realm for backend-to-backend requests (bypasses external DNS). + # When set, oauth2-proxy uses --skip-oidc-discovery and routes all backend calls (token, jwks, + # userinfo, logout) through this URL while keeping browser redirects on the external URL. + # Example: http://keycloak-http.cozy-keycloak.svc:8080/realms/cozy + keycloakInternalUrl: "" # Pod scheduling configuration scheduling: globalAppTopologySpreadConstraints: "" diff --git a/packages/core/talos/images/talos/profiles/initramfs.yaml b/packages/core/talos/images/talos/profiles/initramfs.yaml index 20bc27d3..182fb3e8 100644 --- a/packages/core/talos/images/talos/profiles/initramfs.yaml +++ b/packages/core/talos/images/talos/profiles/initramfs.yaml @@ -3,24 +3,24 @@ arch: amd64 platform: metal secureboot: false -version: v1.12.1 +version: v1.12.6 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz baseInstaller: - imageRef: "ghcr.io/siderolabs/installer:v1.12.1" + imageRef: "ghcr.io/siderolabs/installer:v1.12.6" systemExtensions: - - imageRef: ghcr.io/siderolabs/amd-ucode:20251125@sha256:aa2c684933d28cf10ef785f0d94f91d6d098e164374114648867cf81c2b585fe - - imageRef: ghcr.io/siderolabs/amdgpu:20251125-v1.12.1@sha256:b73aba10ac51cd0d74a6c45210ccee3f6b7d2d97f9b3151d0563b11aa0727599 - - imageRef: ghcr.io/siderolabs/bnx2-bnx2x:20251125@sha256:fe33c69c471a8d097c58ab9e5e1e099c3c6e5bb785e74f6f135fdbd71c3b0feb - - imageRef: ghcr.io/siderolabs/intel-ice-firmware:20251125@sha256:01458f60448e166eeb641ee989b941725cbe6759e10afe2251c6d1b1ca5ba1b7 - - imageRef: ghcr.io/siderolabs/i915:20251125-v1.12.1@sha256:fb89c85a04ecb85abaec9d400e03a1628bf68aef3580e98f340cbe8920a6e4ed - - imageRef: ghcr.io/siderolabs/intel-ucode:20251111@sha256:51b7d1c31cb82f340a96228f1870b1e829e8ed2dfeef6d39926975303736d26a - - imageRef: ghcr.io/siderolabs/qlogic-firmware:20251125@sha256:485ef0a0b58328ded511e9a76014c353da24bb147c8b2929068827e5f9a22326 - - imageRef: ghcr.io/siderolabs/drbd:9.2.16-v1.12.1@sha256:2c0dc35d5f3e1ac23de6eeee5554d9da010ac848a733a538c9568a9ccc782d86 - - imageRef: ghcr.io/siderolabs/zfs:2.4.0-v1.12.1@sha256:926f4cdaaa2cfad09f080eda5cfd08b8ba0bad083df3ca59e9764f4104539246 + - imageRef: ghcr.io/siderolabs/amd-ucode:20260309@sha256:716cd5350f77e7b10eebef88d8abdd0c32f2d929acd2bceac7ea33c0aac3b1a7 + - imageRef: ghcr.io/siderolabs/amdgpu:20260309-v1.12.6@sha256:64c7cbcde57a69742c76e3899385f5cd6af1238b0aba094962197fd4098838bd + - imageRef: ghcr.io/siderolabs/bnx2-bnx2x:20260309@sha256:b17b87d9a20c806cf186e95ab351b771ffd622a35afcbd41eb07d4642680a231 + - imageRef: ghcr.io/siderolabs/intel-ice-firmware:20260309@sha256:eee55c66cb23fe93380b83909af2cd4ede970205b861c0aa147404c88e8c7f65 + - imageRef: ghcr.io/siderolabs/i915:20260309-v1.12.6@sha256:6d150e23b2ca98ed34fca2b0510bc97e912f95839926bf6e02c55952e15387c2 + - imageRef: ghcr.io/siderolabs/intel-ucode:20260227@sha256:6112f6426a7081727545f7c33dcf2a89ff672494847b180380b4c58f3544a7b2 + - imageRef: ghcr.io/siderolabs/qlogic-firmware:20260309@sha256:b97cfde1e252412ba594a16302a3505f80aee1f105e2aa76b3bdf3b9dcc56cab + - imageRef: ghcr.io/siderolabs/drbd:9.2.16-v1.12.6@sha256:f524139c5be4626d7f2b6bd47745ce93648f3f1ab2b681fb00f8ab97ec2c19a3 + - imageRef: ghcr.io/siderolabs/zfs:2.4.1-v1.12.6@sha256:059d39f1d2d6dde263dbd8062207d0aa68805bb0748c087494791c7f985b87d1 output: kind: initramfs imageOptions: {} diff --git a/packages/core/talos/images/talos/profiles/installer.yaml b/packages/core/talos/images/talos/profiles/installer.yaml index b219e012..c56c1d32 100644 --- a/packages/core/talos/images/talos/profiles/installer.yaml +++ b/packages/core/talos/images/talos/profiles/installer.yaml @@ -3,24 +3,24 @@ arch: amd64 platform: metal secureboot: false -version: v1.12.1 +version: v1.12.6 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz baseInstaller: - imageRef: "ghcr.io/siderolabs/installer:v1.12.1" + imageRef: "ghcr.io/siderolabs/installer:v1.12.6" systemExtensions: - - imageRef: ghcr.io/siderolabs/amd-ucode:20251125@sha256:aa2c684933d28cf10ef785f0d94f91d6d098e164374114648867cf81c2b585fe - - imageRef: ghcr.io/siderolabs/amdgpu:20251125-v1.12.1@sha256:b73aba10ac51cd0d74a6c45210ccee3f6b7d2d97f9b3151d0563b11aa0727599 - - imageRef: ghcr.io/siderolabs/bnx2-bnx2x:20251125@sha256:fe33c69c471a8d097c58ab9e5e1e099c3c6e5bb785e74f6f135fdbd71c3b0feb - - imageRef: ghcr.io/siderolabs/intel-ice-firmware:20251125@sha256:01458f60448e166eeb641ee989b941725cbe6759e10afe2251c6d1b1ca5ba1b7 - - imageRef: ghcr.io/siderolabs/i915:20251125-v1.12.1@sha256:fb89c85a04ecb85abaec9d400e03a1628bf68aef3580e98f340cbe8920a6e4ed - - imageRef: ghcr.io/siderolabs/intel-ucode:20251111@sha256:51b7d1c31cb82f340a96228f1870b1e829e8ed2dfeef6d39926975303736d26a - - imageRef: ghcr.io/siderolabs/qlogic-firmware:20251125@sha256:485ef0a0b58328ded511e9a76014c353da24bb147c8b2929068827e5f9a22326 - - imageRef: ghcr.io/siderolabs/drbd:9.2.16-v1.12.1@sha256:2c0dc35d5f3e1ac23de6eeee5554d9da010ac848a733a538c9568a9ccc782d86 - - imageRef: ghcr.io/siderolabs/zfs:2.4.0-v1.12.1@sha256:926f4cdaaa2cfad09f080eda5cfd08b8ba0bad083df3ca59e9764f4104539246 + - imageRef: ghcr.io/siderolabs/amd-ucode:20260309@sha256:716cd5350f77e7b10eebef88d8abdd0c32f2d929acd2bceac7ea33c0aac3b1a7 + - imageRef: ghcr.io/siderolabs/amdgpu:20260309-v1.12.6@sha256:64c7cbcde57a69742c76e3899385f5cd6af1238b0aba094962197fd4098838bd + - imageRef: ghcr.io/siderolabs/bnx2-bnx2x:20260309@sha256:b17b87d9a20c806cf186e95ab351b771ffd622a35afcbd41eb07d4642680a231 + - imageRef: ghcr.io/siderolabs/intel-ice-firmware:20260309@sha256:eee55c66cb23fe93380b83909af2cd4ede970205b861c0aa147404c88e8c7f65 + - imageRef: ghcr.io/siderolabs/i915:20260309-v1.12.6@sha256:6d150e23b2ca98ed34fca2b0510bc97e912f95839926bf6e02c55952e15387c2 + - imageRef: ghcr.io/siderolabs/intel-ucode:20260227@sha256:6112f6426a7081727545f7c33dcf2a89ff672494847b180380b4c58f3544a7b2 + - imageRef: ghcr.io/siderolabs/qlogic-firmware:20260309@sha256:b97cfde1e252412ba594a16302a3505f80aee1f105e2aa76b3bdf3b9dcc56cab + - imageRef: ghcr.io/siderolabs/drbd:9.2.16-v1.12.6@sha256:f524139c5be4626d7f2b6bd47745ce93648f3f1ab2b681fb00f8ab97ec2c19a3 + - imageRef: ghcr.io/siderolabs/zfs:2.4.1-v1.12.6@sha256:059d39f1d2d6dde263dbd8062207d0aa68805bb0748c087494791c7f985b87d1 output: kind: installer imageOptions: {} diff --git a/packages/core/talos/images/talos/profiles/iso.yaml b/packages/core/talos/images/talos/profiles/iso.yaml index 5773c6a6..25fc336a 100644 --- a/packages/core/talos/images/talos/profiles/iso.yaml +++ b/packages/core/talos/images/talos/profiles/iso.yaml @@ -3,24 +3,24 @@ arch: amd64 platform: metal secureboot: false -version: v1.12.1 +version: v1.12.6 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz baseInstaller: - imageRef: "ghcr.io/siderolabs/installer:v1.12.1" + imageRef: "ghcr.io/siderolabs/installer:v1.12.6" systemExtensions: - - imageRef: ghcr.io/siderolabs/amd-ucode:20251125@sha256:aa2c684933d28cf10ef785f0d94f91d6d098e164374114648867cf81c2b585fe - - imageRef: ghcr.io/siderolabs/amdgpu:20251125-v1.12.1@sha256:b73aba10ac51cd0d74a6c45210ccee3f6b7d2d97f9b3151d0563b11aa0727599 - - imageRef: ghcr.io/siderolabs/bnx2-bnx2x:20251125@sha256:fe33c69c471a8d097c58ab9e5e1e099c3c6e5bb785e74f6f135fdbd71c3b0feb - - imageRef: ghcr.io/siderolabs/intel-ice-firmware:20251125@sha256:01458f60448e166eeb641ee989b941725cbe6759e10afe2251c6d1b1ca5ba1b7 - - imageRef: ghcr.io/siderolabs/i915:20251125-v1.12.1@sha256:fb89c85a04ecb85abaec9d400e03a1628bf68aef3580e98f340cbe8920a6e4ed - - imageRef: ghcr.io/siderolabs/intel-ucode:20251111@sha256:51b7d1c31cb82f340a96228f1870b1e829e8ed2dfeef6d39926975303736d26a - - imageRef: ghcr.io/siderolabs/qlogic-firmware:20251125@sha256:485ef0a0b58328ded511e9a76014c353da24bb147c8b2929068827e5f9a22326 - - imageRef: ghcr.io/siderolabs/drbd:9.2.16-v1.12.1@sha256:2c0dc35d5f3e1ac23de6eeee5554d9da010ac848a733a538c9568a9ccc782d86 - - imageRef: ghcr.io/siderolabs/zfs:2.4.0-v1.12.1@sha256:926f4cdaaa2cfad09f080eda5cfd08b8ba0bad083df3ca59e9764f4104539246 + - imageRef: ghcr.io/siderolabs/amd-ucode:20260309@sha256:716cd5350f77e7b10eebef88d8abdd0c32f2d929acd2bceac7ea33c0aac3b1a7 + - imageRef: ghcr.io/siderolabs/amdgpu:20260309-v1.12.6@sha256:64c7cbcde57a69742c76e3899385f5cd6af1238b0aba094962197fd4098838bd + - imageRef: ghcr.io/siderolabs/bnx2-bnx2x:20260309@sha256:b17b87d9a20c806cf186e95ab351b771ffd622a35afcbd41eb07d4642680a231 + - imageRef: ghcr.io/siderolabs/intel-ice-firmware:20260309@sha256:eee55c66cb23fe93380b83909af2cd4ede970205b861c0aa147404c88e8c7f65 + - imageRef: ghcr.io/siderolabs/i915:20260309-v1.12.6@sha256:6d150e23b2ca98ed34fca2b0510bc97e912f95839926bf6e02c55952e15387c2 + - imageRef: ghcr.io/siderolabs/intel-ucode:20260227@sha256:6112f6426a7081727545f7c33dcf2a89ff672494847b180380b4c58f3544a7b2 + - imageRef: ghcr.io/siderolabs/qlogic-firmware:20260309@sha256:b97cfde1e252412ba594a16302a3505f80aee1f105e2aa76b3bdf3b9dcc56cab + - imageRef: ghcr.io/siderolabs/drbd:9.2.16-v1.12.6@sha256:f524139c5be4626d7f2b6bd47745ce93648f3f1ab2b681fb00f8ab97ec2c19a3 + - imageRef: ghcr.io/siderolabs/zfs:2.4.1-v1.12.6@sha256:059d39f1d2d6dde263dbd8062207d0aa68805bb0748c087494791c7f985b87d1 output: kind: iso imageOptions: {} diff --git a/packages/core/talos/images/talos/profiles/kernel.yaml b/packages/core/talos/images/talos/profiles/kernel.yaml index 2f80bbda..d0153bf8 100644 --- a/packages/core/talos/images/talos/profiles/kernel.yaml +++ b/packages/core/talos/images/talos/profiles/kernel.yaml @@ -3,24 +3,24 @@ arch: amd64 platform: metal secureboot: false -version: v1.12.1 +version: v1.12.6 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz baseInstaller: - imageRef: "ghcr.io/siderolabs/installer:v1.12.1" + imageRef: "ghcr.io/siderolabs/installer:v1.12.6" systemExtensions: - - imageRef: ghcr.io/siderolabs/amd-ucode:20251125@sha256:aa2c684933d28cf10ef785f0d94f91d6d098e164374114648867cf81c2b585fe - - imageRef: ghcr.io/siderolabs/amdgpu:20251125-v1.12.1@sha256:b73aba10ac51cd0d74a6c45210ccee3f6b7d2d97f9b3151d0563b11aa0727599 - - imageRef: ghcr.io/siderolabs/bnx2-bnx2x:20251125@sha256:fe33c69c471a8d097c58ab9e5e1e099c3c6e5bb785e74f6f135fdbd71c3b0feb - - imageRef: ghcr.io/siderolabs/intel-ice-firmware:20251125@sha256:01458f60448e166eeb641ee989b941725cbe6759e10afe2251c6d1b1ca5ba1b7 - - imageRef: ghcr.io/siderolabs/i915:20251125-v1.12.1@sha256:fb89c85a04ecb85abaec9d400e03a1628bf68aef3580e98f340cbe8920a6e4ed - - imageRef: ghcr.io/siderolabs/intel-ucode:20251111@sha256:51b7d1c31cb82f340a96228f1870b1e829e8ed2dfeef6d39926975303736d26a - - imageRef: ghcr.io/siderolabs/qlogic-firmware:20251125@sha256:485ef0a0b58328ded511e9a76014c353da24bb147c8b2929068827e5f9a22326 - - imageRef: ghcr.io/siderolabs/drbd:9.2.16-v1.12.1@sha256:2c0dc35d5f3e1ac23de6eeee5554d9da010ac848a733a538c9568a9ccc782d86 - - imageRef: ghcr.io/siderolabs/zfs:2.4.0-v1.12.1@sha256:926f4cdaaa2cfad09f080eda5cfd08b8ba0bad083df3ca59e9764f4104539246 + - imageRef: ghcr.io/siderolabs/amd-ucode:20260309@sha256:716cd5350f77e7b10eebef88d8abdd0c32f2d929acd2bceac7ea33c0aac3b1a7 + - imageRef: ghcr.io/siderolabs/amdgpu:20260309-v1.12.6@sha256:64c7cbcde57a69742c76e3899385f5cd6af1238b0aba094962197fd4098838bd + - imageRef: ghcr.io/siderolabs/bnx2-bnx2x:20260309@sha256:b17b87d9a20c806cf186e95ab351b771ffd622a35afcbd41eb07d4642680a231 + - imageRef: ghcr.io/siderolabs/intel-ice-firmware:20260309@sha256:eee55c66cb23fe93380b83909af2cd4ede970205b861c0aa147404c88e8c7f65 + - imageRef: ghcr.io/siderolabs/i915:20260309-v1.12.6@sha256:6d150e23b2ca98ed34fca2b0510bc97e912f95839926bf6e02c55952e15387c2 + - imageRef: ghcr.io/siderolabs/intel-ucode:20260227@sha256:6112f6426a7081727545f7c33dcf2a89ff672494847b180380b4c58f3544a7b2 + - imageRef: ghcr.io/siderolabs/qlogic-firmware:20260309@sha256:b97cfde1e252412ba594a16302a3505f80aee1f105e2aa76b3bdf3b9dcc56cab + - imageRef: ghcr.io/siderolabs/drbd:9.2.16-v1.12.6@sha256:f524139c5be4626d7f2b6bd47745ce93648f3f1ab2b681fb00f8ab97ec2c19a3 + - imageRef: ghcr.io/siderolabs/zfs:2.4.1-v1.12.6@sha256:059d39f1d2d6dde263dbd8062207d0aa68805bb0748c087494791c7f985b87d1 output: kind: kernel imageOptions: {} diff --git a/packages/core/talos/images/talos/profiles/metal.yaml b/packages/core/talos/images/talos/profiles/metal.yaml index d460e0d8..579cc333 100644 --- a/packages/core/talos/images/talos/profiles/metal.yaml +++ b/packages/core/talos/images/talos/profiles/metal.yaml @@ -3,24 +3,24 @@ arch: amd64 platform: metal secureboot: false -version: v1.12.1 +version: v1.12.6 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz baseInstaller: - imageRef: "ghcr.io/siderolabs/installer:v1.12.1" + imageRef: "ghcr.io/siderolabs/installer:v1.12.6" systemExtensions: - - imageRef: ghcr.io/siderolabs/amd-ucode:20251125@sha256:aa2c684933d28cf10ef785f0d94f91d6d098e164374114648867cf81c2b585fe - - imageRef: ghcr.io/siderolabs/amdgpu:20251125-v1.12.1@sha256:b73aba10ac51cd0d74a6c45210ccee3f6b7d2d97f9b3151d0563b11aa0727599 - - imageRef: ghcr.io/siderolabs/bnx2-bnx2x:20251125@sha256:fe33c69c471a8d097c58ab9e5e1e099c3c6e5bb785e74f6f135fdbd71c3b0feb - - imageRef: ghcr.io/siderolabs/intel-ice-firmware:20251125@sha256:01458f60448e166eeb641ee989b941725cbe6759e10afe2251c6d1b1ca5ba1b7 - - imageRef: ghcr.io/siderolabs/i915:20251125-v1.12.1@sha256:fb89c85a04ecb85abaec9d400e03a1628bf68aef3580e98f340cbe8920a6e4ed - - imageRef: ghcr.io/siderolabs/intel-ucode:20251111@sha256:51b7d1c31cb82f340a96228f1870b1e829e8ed2dfeef6d39926975303736d26a - - imageRef: ghcr.io/siderolabs/qlogic-firmware:20251125@sha256:485ef0a0b58328ded511e9a76014c353da24bb147c8b2929068827e5f9a22326 - - imageRef: ghcr.io/siderolabs/drbd:9.2.16-v1.12.1@sha256:2c0dc35d5f3e1ac23de6eeee5554d9da010ac848a733a538c9568a9ccc782d86 - - imageRef: ghcr.io/siderolabs/zfs:2.4.0-v1.12.1@sha256:926f4cdaaa2cfad09f080eda5cfd08b8ba0bad083df3ca59e9764f4104539246 + - imageRef: ghcr.io/siderolabs/amd-ucode:20260309@sha256:716cd5350f77e7b10eebef88d8abdd0c32f2d929acd2bceac7ea33c0aac3b1a7 + - imageRef: ghcr.io/siderolabs/amdgpu:20260309-v1.12.6@sha256:64c7cbcde57a69742c76e3899385f5cd6af1238b0aba094962197fd4098838bd + - imageRef: ghcr.io/siderolabs/bnx2-bnx2x:20260309@sha256:b17b87d9a20c806cf186e95ab351b771ffd622a35afcbd41eb07d4642680a231 + - imageRef: ghcr.io/siderolabs/intel-ice-firmware:20260309@sha256:eee55c66cb23fe93380b83909af2cd4ede970205b861c0aa147404c88e8c7f65 + - imageRef: ghcr.io/siderolabs/i915:20260309-v1.12.6@sha256:6d150e23b2ca98ed34fca2b0510bc97e912f95839926bf6e02c55952e15387c2 + - imageRef: ghcr.io/siderolabs/intel-ucode:20260227@sha256:6112f6426a7081727545f7c33dcf2a89ff672494847b180380b4c58f3544a7b2 + - imageRef: ghcr.io/siderolabs/qlogic-firmware:20260309@sha256:b97cfde1e252412ba594a16302a3505f80aee1f105e2aa76b3bdf3b9dcc56cab + - imageRef: ghcr.io/siderolabs/drbd:9.2.16-v1.12.6@sha256:f524139c5be4626d7f2b6bd47745ce93648f3f1ab2b681fb00f8ab97ec2c19a3 + - imageRef: ghcr.io/siderolabs/zfs:2.4.1-v1.12.6@sha256:059d39f1d2d6dde263dbd8062207d0aa68805bb0748c087494791c7f985b87d1 output: kind: image imageOptions: { diskSize: 1306525696, diskFormat: raw } diff --git a/packages/core/talos/images/talos/profiles/nocloud.yaml b/packages/core/talos/images/talos/profiles/nocloud.yaml index 3dfb37f7..24ad7f3c 100644 --- a/packages/core/talos/images/talos/profiles/nocloud.yaml +++ b/packages/core/talos/images/talos/profiles/nocloud.yaml @@ -3,24 +3,24 @@ arch: amd64 platform: nocloud secureboot: false -version: v1.12.1 +version: v1.12.6 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz baseInstaller: - imageRef: "ghcr.io/siderolabs/installer:v1.12.1" + imageRef: "ghcr.io/siderolabs/installer:v1.12.6" systemExtensions: - - imageRef: ghcr.io/siderolabs/amd-ucode:20251125@sha256:aa2c684933d28cf10ef785f0d94f91d6d098e164374114648867cf81c2b585fe - - imageRef: ghcr.io/siderolabs/amdgpu:20251125-v1.12.1@sha256:b73aba10ac51cd0d74a6c45210ccee3f6b7d2d97f9b3151d0563b11aa0727599 - - imageRef: ghcr.io/siderolabs/bnx2-bnx2x:20251125@sha256:fe33c69c471a8d097c58ab9e5e1e099c3c6e5bb785e74f6f135fdbd71c3b0feb - - imageRef: ghcr.io/siderolabs/intel-ice-firmware:20251125@sha256:01458f60448e166eeb641ee989b941725cbe6759e10afe2251c6d1b1ca5ba1b7 - - imageRef: ghcr.io/siderolabs/i915:20251125-v1.12.1@sha256:fb89c85a04ecb85abaec9d400e03a1628bf68aef3580e98f340cbe8920a6e4ed - - imageRef: ghcr.io/siderolabs/intel-ucode:20251111@sha256:51b7d1c31cb82f340a96228f1870b1e829e8ed2dfeef6d39926975303736d26a - - imageRef: ghcr.io/siderolabs/qlogic-firmware:20251125@sha256:485ef0a0b58328ded511e9a76014c353da24bb147c8b2929068827e5f9a22326 - - imageRef: ghcr.io/siderolabs/drbd:9.2.16-v1.12.1@sha256:2c0dc35d5f3e1ac23de6eeee5554d9da010ac848a733a538c9568a9ccc782d86 - - imageRef: ghcr.io/siderolabs/zfs:2.4.0-v1.12.1@sha256:926f4cdaaa2cfad09f080eda5cfd08b8ba0bad083df3ca59e9764f4104539246 + - imageRef: ghcr.io/siderolabs/amd-ucode:20260309@sha256:716cd5350f77e7b10eebef88d8abdd0c32f2d929acd2bceac7ea33c0aac3b1a7 + - imageRef: ghcr.io/siderolabs/amdgpu:20260309-v1.12.6@sha256:64c7cbcde57a69742c76e3899385f5cd6af1238b0aba094962197fd4098838bd + - imageRef: ghcr.io/siderolabs/bnx2-bnx2x:20260309@sha256:b17b87d9a20c806cf186e95ab351b771ffd622a35afcbd41eb07d4642680a231 + - imageRef: ghcr.io/siderolabs/intel-ice-firmware:20260309@sha256:eee55c66cb23fe93380b83909af2cd4ede970205b861c0aa147404c88e8c7f65 + - imageRef: ghcr.io/siderolabs/i915:20260309-v1.12.6@sha256:6d150e23b2ca98ed34fca2b0510bc97e912f95839926bf6e02c55952e15387c2 + - imageRef: ghcr.io/siderolabs/intel-ucode:20260227@sha256:6112f6426a7081727545f7c33dcf2a89ff672494847b180380b4c58f3544a7b2 + - imageRef: ghcr.io/siderolabs/qlogic-firmware:20260309@sha256:b97cfde1e252412ba594a16302a3505f80aee1f105e2aa76b3bdf3b9dcc56cab + - imageRef: ghcr.io/siderolabs/drbd:9.2.16-v1.12.6@sha256:f524139c5be4626d7f2b6bd47745ce93648f3f1ab2b681fb00f8ab97ec2c19a3 + - imageRef: ghcr.io/siderolabs/zfs:2.4.1-v1.12.6@sha256:059d39f1d2d6dde263dbd8062207d0aa68805bb0748c087494791c7f985b87d1 output: kind: image imageOptions: { diskSize: 1306525696, diskFormat: raw } diff --git a/packages/core/testing/values.yaml b/packages/core/testing/values.yaml index 0692cab6..9d4bdd10 100644 --- a/packages/core/testing/values.yaml +++ b/packages/core/testing/values.yaml @@ -1,2 +1,2 @@ e2e: - image: ghcr.io/cozystack/cozystack/e2e-sandbox:v1.0.0-beta.6@sha256:09af5901abcbed2b612d2d93c163e8ad3948bc55a1d8beae714b4fb2b8f7d91d + image: ghcr.io/cozystack/cozystack/e2e-sandbox:v1.3.0@sha256:0367a03b981df2a3ea13f411d4cb7869c2bf2c89c07d3d5c8971b9a28921ccef diff --git a/packages/extra/bootbox/images/matchbox.tag b/packages/extra/bootbox/images/matchbox.tag index 9729ce6e..f51c7675 100644 --- a/packages/extra/bootbox/images/matchbox.tag +++ b/packages/extra/bootbox/images/matchbox.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/matchbox:v1.0.0-beta.6@sha256:212f624957447f5a932fd5d4564eb8c97694d336b7dc877a2833c1513c0d074d +ghcr.io/cozystack/cozystack/matchbox:v1.3.0@sha256:85b8e04bf6f0690612dd63e80475df269f4a436d16680f8a40f2860cf16e2f74 diff --git a/packages/extra/bootbox/templates/matchbox/ingress.yaml b/packages/extra/bootbox/templates/matchbox/ingress.yaml index fa62165b..5eef3489 100644 --- a/packages/extra/bootbox/templates/matchbox/ingress.yaml +++ b/packages/extra/bootbox/templates/matchbox/ingress.yaml @@ -1,4 +1,5 @@ -{{- $issuerType := (index .Values._cluster "clusterissuer") | default "http01" }} +{{- $solver := (index .Values._cluster "solver") | default "http01" }} +{{- $clusterIssuer := (index .Values._cluster "issuer-name") | default "letsencrypt-prod" }} {{- $ingress := .Values._namespace.ingress }} {{- $host := .Values._namespace.host }} apiVersion: networking.k8s.io/v1 @@ -8,10 +9,10 @@ metadata: labels: app: bootbox annotations: - {{- if ne $issuerType "cloudflare" }} - acme.cert-manager.io/http01-ingress-class: {{ $ingress }} + {{- if eq $solver "http01" }} + acme.cert-manager.io/http01-ingress-ingressclassname: {{ $ingress }} {{- end }} - cert-manager.io/cluster-issuer: letsencrypt-prod + cert-manager.io/cluster-issuer: {{ $clusterIssuer }} {{- if .Values.whitelistHTTP }} nginx.ingress.kubernetes.io/whitelist-source-range: "{{ join "," (.Values.whitelist | default "0.0.0.0/32") }}" {{- end }} diff --git a/packages/extra/bootbox/values.schema.json b/packages/extra/bootbox/values.schema.json index 994f47cd..8e243c12 100644 --- a/packages/extra/bootbox/values.schema.json +++ b/packages/extra/bootbox/values.schema.json @@ -2,6 +2,19 @@ "title": "Chart Values", "type": "object", "properties": { + "whitelistHTTP": { + "description": "Secure HTTP by enabling client networks whitelisting.", + "type": "boolean", + "default": true + }, + "whitelist": { + "description": "List of client networks.", + "type": "array", + "default": [], + "items": { + "type": "string" + } + }, "machines": { "description": "Configuration of physical machine instances.", "type": "array", @@ -78,19 +91,6 @@ } } } - }, - "whitelist": { - "description": "List of client networks.", - "type": "array", - "default": [], - "items": { - "type": "string" - } - }, - "whitelistHTTP": { - "description": "Secure HTTP by enabling client networks whitelisting.", - "type": "boolean", - "default": true } } -} \ No newline at end of file +} diff --git a/packages/extra/etcd/Makefile b/packages/extra/etcd/Makefile index f37d6e1e..2b3ed61e 100644 --- a/packages/extra/etcd/Makefile +++ b/packages/extra/etcd/Makefile @@ -5,3 +5,6 @@ include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md ../../../hack/update-crd.sh + +test: + helm unittest . diff --git a/packages/extra/etcd/templates/etcd-cluster.yaml b/packages/extra/etcd/templates/etcd-cluster.yaml index 44851cc6..a604b448 100644 --- a/packages/extra/etcd/templates/etcd-cluster.yaml +++ b/packages/extra/etcd/templates/etcd-cluster.yaml @@ -104,6 +104,7 @@ spec: - {{ .Release.Name }} secretName: etcd-peer-ca-tls privateKey: + rotationPolicy: Never algorithm: RSA size: 4096 issuerRef: @@ -130,6 +131,7 @@ spec: - {{ .Release.Name }} secretName: etcd-ca-tls privateKey: + rotationPolicy: Never algorithm: RSA size: 4096 issuerRef: diff --git a/packages/extra/etcd/templates/etcd-defrag.yaml b/packages/extra/etcd/templates/etcd-defrag.yaml index 089df920..0478129f 100644 --- a/packages/extra/etcd/templates/etcd-defrag.yaml +++ b/packages/extra/etcd/templates/etcd-defrag.yaml @@ -4,9 +4,14 @@ metadata: name: {{ .Release.Name }}-defrag spec: schedule: "0 * * * *" + concurrencyPolicy: Forbid + startingDeadlineSeconds: 300 successfulJobsHistoryLimit: 3 + failedJobsHistoryLimit: 1 jobTemplate: spec: + activeDeadlineSeconds: 1800 + backoffLimit: 2 template: spec: containers: diff --git a/packages/extra/etcd/templates/hook/job.yaml b/packages/extra/etcd/templates/hook/job.yaml deleted file mode 100644 index 3bf2f84b..00000000 --- a/packages/extra/etcd/templates/hook/job.yaml +++ /dev/null @@ -1,39 +0,0 @@ -{{- $shouldUpdateCerts := true }} -{{- $configMap := lookup "v1" "ConfigMap" .Release.Namespace "etcd-deployed-version" }} -{{- if $configMap }} - {{- $deployedVersion := index $configMap "data" "version" }} - {{- if $deployedVersion | semverCompare ">= 2.6.1" }} - {{- $shouldUpdateCerts = false }} - {{- end }} -{{- end }} - -{{- if $shouldUpdateCerts }} ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: etcd-hook - annotations: - helm.sh/hook: post-upgrade - helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded -spec: - template: - metadata: - labels: - policy.cozystack.io/allow-to-apiserver: "true" - spec: - serviceAccountName: etcd-hook - containers: - - name: kubectl - image: docker.io/alpine/k8s:1.33.4 - command: - - sh - args: - - -exc - - |- - kubectl --namespace={{ .Release.Namespace }} delete secrets etcd-ca-tls etcd-peer-ca-tls - sleep 10 - kubectl --namespace={{ .Release.Namespace }} delete secrets etcd-client-tls etcd-peer-tls etcd-server-tls - kubectl --namespace={{ .Release.Namespace }} delete pods --selector=app.kubernetes.io/instance=etcd,app.kubernetes.io/managed-by=etcd-operator,app.kubernetes.io/name=etcd,cozystack.io/service=etcd - restartPolicy: Never -{{- end }} diff --git a/packages/extra/etcd/templates/hook/role.yaml b/packages/extra/etcd/templates/hook/role.yaml deleted file mode 100644 index 327eeadb..00000000 --- a/packages/extra/etcd/templates/hook/role.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - annotations: - helm.sh/hook: post-upgrade - helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded - name: etcd-hook -rules: -- apiGroups: - - "" - resources: - - secrets - - pods - verbs: - - get - - list - - watch - - delete -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch diff --git a/packages/extra/etcd/templates/hook/rolebinding.yaml b/packages/extra/etcd/templates/hook/rolebinding.yaml deleted file mode 100644 index 0ee0ffd1..00000000 --- a/packages/extra/etcd/templates/hook/rolebinding.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: etcd-hook - annotations: - helm.sh/hook: post-upgrade - helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: etcd-hook -subjects: - - kind: ServiceAccount - name: etcd-hook - namespace: {{ .Release.Namespace | quote }} diff --git a/packages/extra/etcd/templates/hook/serviceaccount.yaml b/packages/extra/etcd/templates/hook/serviceaccount.yaml deleted file mode 100644 index 552fb5fc..00000000 --- a/packages/extra/etcd/templates/hook/serviceaccount.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount -metadata: - name: etcd-hook - annotations: - helm.sh/hook: post-upgrade - helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded diff --git a/packages/extra/etcd/templates/version.yaml b/packages/extra/etcd/templates/version.yaml deleted file mode 100644 index cc9375bb..00000000 --- a/packages/extra/etcd/templates/version.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: etcd-deployed-version -data: - version: {{ .Chart.Version }} diff --git a/packages/extra/etcd/tests/no-post-upgrade-hook_test.yaml b/packages/extra/etcd/tests/no-post-upgrade-hook_test.yaml new file mode 100644 index 00000000..0e4f5aa3 --- /dev/null +++ b/packages/extra/etcd/tests/no-post-upgrade-hook_test.yaml @@ -0,0 +1,34 @@ +suite: etcd chart does not ship a destructive post-upgrade cert-regeneration hook + +release: + name: etcd + namespace: tenant-root + +templates: + - templates/check-release-name.yaml + - templates/dashboard-resourcemap.yaml + - templates/datastore.yaml + - templates/etcd-defrag.yaml + - templates/hook/job.yaml + - templates/podscrape.yaml + - templates/prometheus-rules.yaml + - templates/version.yaml + +tests: + - it: renders no Job named etcd-hook + documentSelector: + path: metadata.name + value: etcd-hook + skipEmptyTemplates: true + asserts: + - hasDocuments: + count: 0 + + - it: renders no ConfigMap named etcd-deployed-version + documentSelector: + path: metadata.name + value: etcd-deployed-version + skipEmptyTemplates: true + asserts: + - hasDocuments: + count: 0 diff --git a/packages/extra/etcd/values.schema.json b/packages/extra/etcd/values.schema.json index 0f696c57..3c66664c 100644 --- a/packages/extra/etcd/values.schema.json +++ b/packages/extra/etcd/values.schema.json @@ -2,6 +2,25 @@ "title": "Chart Values", "type": "object", "properties": { + "size": { + "description": "Persistent Volume size.", + "default": "4Gi", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "storageClass": { + "description": "StorageClass used to store the data.", + "type": "string", + "default": "" + }, "replicas": { "description": "Number of etcd replicas.", "type": "integer", @@ -41,25 +60,6 @@ "x-kubernetes-int-or-string": true } } - }, - "size": { - "description": "Persistent Volume size.", - "default": "4Gi", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - }, - "storageClass": { - "description": "StorageClass used to store the data.", - "type": "string", - "default": "" } } -} \ No newline at end of file +} diff --git a/packages/extra/external-dns/Chart.yaml b/packages/extra/external-dns/Chart.yaml new file mode 100644 index 00000000..d9788a41 --- /dev/null +++ b/packages/extra/external-dns/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: external-dns +description: External DNS for automatic DNS record management +icon: /logos/external-dns.svg +type: application +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/extra/external-dns/Makefile b/packages/extra/external-dns/Makefile new file mode 100644 index 00000000..8b8fbc2f --- /dev/null +++ b/packages/extra/external-dns/Makefile @@ -0,0 +1,7 @@ +NAME=external-dns + +include ../../../hack/package.mk + +generate: + cozyvalues-gen -v values.yaml -s values.schema.json -r README.md + ../../../hack/update-crd.sh diff --git a/packages/extra/external-dns/README.md b/packages/extra/external-dns/README.md new file mode 100644 index 00000000..027182c1 --- /dev/null +++ b/packages/extra/external-dns/README.md @@ -0,0 +1,88 @@ +# External DNS + +## Parameters + +### Common parameters + +| Name | Description | Type | Value | +| ------------------ | ---------------------------------------------------------------------------------- | ---------- | ------------- | +| `provider` | DNS provider name. | `string` | `{}` | +| `domainFilters` | List of domains this external-dns instance can manage. | `[]string` | `[]` | +| `policy` | How DNS records are synchronized. | `string` | `upsert-only` | +| `extraArgs` | Extra arguments for external-dns. | `[]string` | `[]` | +| `gatewayAPI` | Enable Gateway API HTTPRoute as a source for DNS records. | `bool` | `false` | +| `annotationPrefix` | Custom annotation prefix for external-dns (useful for running multiple instances). | `string` | `""` | + + +### Cloudflare + +| Name | Description | Type | Value | +| ------------ | -------------------------------- | -------- | ----------------------------------------------------------- | +| `cloudflare` | Cloudflare provider credentials. | `object` | `{"apiEmail":"","apiKey":"","apiToken":"","proxied":false}` | + + +### AWS + +| Name | Description | Type | Value | +| ----- | --------------------------------- | -------- | ------------------------------------------------------------------- | +| `aws` | AWS Route53 provider credentials. | `object` | `{"accessKeyId":"","region":"","secretAccessKey":"","zoneType":""}` | + + +### Azure + +| Name | Description | Type | Value | +| ------- | ------------------------------- | -------- | ---------------------------------------------------------------------------------------------- | +| `azure` | Azure DNS provider credentials. | `object` | `{"aadClientId":"","aadClientSecret":"","resourceGroup":"","subscriptionId":"","tenantId":""}` | + + +### Google + +| Name | Description | Type | Value | +| -------- | -------------------------------------- | -------- | --------------------------------------- | +| `google` | Google Cloud DNS provider credentials. | `object` | `{"project":"","serviceAccountKey":""}` | + + +### DigitalOcean + +| Name | Description | Type | Value | +| -------------- | -------------------------------------- | -------- | -------------- | +| `digitalocean` | DigitalOcean DNS provider credentials. | `object` | `{"token":""}` | + + +### Linode + +| Name | Description | Type | Value | +| -------- | -------------------------------- | -------- | -------------- | +| `linode` | Linode DNS provider credentials. | `object` | `{"token":""}` | + + +### OVH + +| Name | Description | Type | Value | +| ----- | ----------------------------- | -------- | ----------------------------------------------------------------------------- | +| `ovh` | OVH DNS provider credentials. | `object` | `{"applicationKey":"","applicationSecret":"","consumerKey":"","endpoint":""}` | + + +### Exoscale + +| Name | Description | Type | Value | +| ---------- | ---------------------------------- | -------- | ------------------------------ | +| `exoscale` | Exoscale DNS provider credentials. | `object` | `{"apiKey":"","apiSecret":""}` | + + +### GoDaddy + +| Name | Description | Type | Value | +| --------- | --------------------------------- | -------- | ------------------------------ | +| `godaddy` | GoDaddy DNS provider credentials. | `object` | `{"apiKey":"","apiSecret":""}` | + + +### Resources + +| Name | Description | Type | Value | +| ------------------ | -------------------------------------------------------------------------------------------------------- | ---------- | ------ | +| `resources` | Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` | +| `resources.cpu` | CPU available to each replica. | `quantity` | `""` | +| `resources.memory` | Memory (RAM) available to each replica. | `quantity` | `""` | +| `resourcesPreset` | Default sizing preset used when `resources` is omitted. | `string` | `nano` | + diff --git a/packages/extra/external-dns/charts/cozy-lib b/packages/extra/external-dns/charts/cozy-lib new file mode 120000 index 00000000..e1813509 --- /dev/null +++ b/packages/extra/external-dns/charts/cozy-lib @@ -0,0 +1 @@ +../../../library/cozy-lib \ No newline at end of file diff --git a/packages/extra/external-dns/config.json b/packages/extra/external-dns/config.json new file mode 100644 index 00000000..b2f956f3 --- /dev/null +++ b/packages/extra/external-dns/config.json @@ -0,0 +1,23 @@ +{ + "comments": { + "format": "##" + }, + "tags": { + "param": "@param", + "section": "@section", + "descriptionStart": "@descriptionStart", + "descriptionEnd": "@descriptionEnd", + "skip": "@skip", + "extra": "@extra" + }, + "modifiers": { + "array": "array", + "object": "object", + "string": "string", + "nullable": "nullable", + "default": "default" + }, + "regexp": { + "paramsSectionTitle": "Parameters" + } +} diff --git a/packages/extra/external-dns/logos/external-dns.svg b/packages/extra/external-dns/logos/external-dns.svg new file mode 100644 index 00000000..b22ad8c4 --- /dev/null +++ b/packages/extra/external-dns/logos/external-dns.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/packages/extra/external-dns/templates/dashboard-resourcemap.yaml b/packages/extra/external-dns/templates/dashboard-resourcemap.yaml new file mode 100644 index 00000000..edd96b74 --- /dev/null +++ b/packages/extra/external-dns/templates/dashboard-resourcemap.yaml @@ -0,0 +1,23 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ .Release.Name }}-dashboard-resources +rules: +- apiGroups: + - helm.toolkit.fluxcd.io + resources: + - helmreleases + resourceNames: + - {{ .Release.Name }} + verbs: ["get", "list", "watch"] +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Release.Name }}-dashboard-resources +subjects: +{{ include "cozy-lib.rbac.subjectsForTenantAndAccessLevel" (list "admin" .Release.Namespace) }} +roleRef: + kind: Role + name: {{ .Release.Name }}-dashboard-resources + apiGroup: rbac.authorization.k8s.io diff --git a/packages/extra/external-dns/templates/external-dns.yaml b/packages/extra/external-dns/templates/external-dns.yaml new file mode 100644 index 00000000..cdfb8901 --- /dev/null +++ b/packages/extra/external-dns/templates/external-dns.yaml @@ -0,0 +1,169 @@ +{{- if not .Values.provider }} +{{- fail "provider is required: set it to one of cloudflare, aws, azure, google, digitalocean, linode, ovh, exoscale, godaddy, inmemory" }} +{{- end }} +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: {{ .Release.Name }}-system + labels: + app.kubernetes.io/instance: {{ .Release.Name }}-system + app.kubernetes.io/managed-by: {{ .Release.Service }} +spec: + chartRef: + kind: ExternalArtifact + name: cozystack-external-dns-application-default-external-dns-system + namespace: cozy-system + interval: 5m + timeout: 10m + install: + remediation: + retries: -1 + upgrade: + force: true + remediation: + retries: -1 + values: + external-dns: + namespaced: true + txtOwnerId: {{ .Release.Namespace | quote }} + policy: {{ .Values.policy | quote }} + {{- if .Values.annotationPrefix }} + annotationPrefix: {{ .Values.annotationPrefix | quote }} + {{- end }} + provider: + name: {{ .Values.provider | quote }} + sources: + - ingress + - service + {{- if .Values.gatewayAPI }} + - gateway-httproute + {{- end }} + {{- with .Values.domainFilters }} + domainFilters: + {{- toYaml . | nindent 8 }} + {{- end }} + + {{- /* Provider-specific credentials and config */}} + + {{- if eq .Values.provider "cloudflare" }} + {{- if or .Values.cloudflare.apiToken (and .Values.cloudflare.apiKey .Values.cloudflare.apiEmail) }} + env: + {{- if .Values.cloudflare.apiToken }} + - name: CF_API_TOKEN + value: {{ .Values.cloudflare.apiToken | quote }} + {{- else }} + - name: CF_API_KEY + value: {{ .Values.cloudflare.apiKey | quote }} + - name: CF_API_EMAIL + value: {{ .Values.cloudflare.apiEmail | quote }} + {{- end }} + {{- end }} + {{- if .Values.cloudflare.proxied }} + cloudflare: + proxied: true + {{- end }} + {{- end }} + + {{- if eq .Values.provider "aws" }} + {{- if or .Values.aws.accessKeyId .Values.aws.secretAccessKey .Values.aws.region }} + env: + {{- if .Values.aws.accessKeyId }} + - name: AWS_ACCESS_KEY_ID + value: {{ .Values.aws.accessKeyId | quote }} + {{- end }} + {{- if .Values.aws.secretAccessKey }} + - name: AWS_SECRET_ACCESS_KEY + value: {{ .Values.aws.secretAccessKey | quote }} + {{- end }} + {{- if .Values.aws.region }} + - name: AWS_DEFAULT_REGION + value: {{ .Values.aws.region | quote }} + {{- end }} + {{- end }} + {{- if .Values.aws.zoneType }} + aws: + zoneType: {{ .Values.aws.zoneType | quote }} + {{- end }} + {{- end }} + + {{- if eq .Values.provider "azure" }} + {{- if or .Values.azure.tenantId .Values.azure.subscriptionId .Values.azure.resourceGroup .Values.azure.aadClientId .Values.azure.aadClientSecret }} + azure: + tenantId: {{ .Values.azure.tenantId | quote }} + subscriptionId: {{ .Values.azure.subscriptionId | quote }} + resourceGroup: {{ .Values.azure.resourceGroup | quote }} + aadClientId: {{ .Values.azure.aadClientId | quote }} + aadClientSecret: {{ .Values.azure.aadClientSecret | quote }} + {{- end }} + {{- end }} + + {{- if eq .Values.provider "digitalocean" }} + {{- if .Values.digitalocean.token }} + env: + - name: DO_TOKEN + value: {{ .Values.digitalocean.token | quote }} + {{- end }} + {{- end }} + + {{- if eq .Values.provider "google" }} + {{- if or .Values.google.project .Values.google.serviceAccountKey }} + google: + project: {{ .Values.google.project | quote }} + {{- if .Values.google.serviceAccountKey }} + serviceAccountKey: {{ .Values.google.serviceAccountKey | quote }} + {{- end }} + {{- end }} + {{- end }} + + {{- if eq .Values.provider "linode" }} + {{- if .Values.linode.token }} + env: + - name: LINODE_TOKEN + value: {{ .Values.linode.token | quote }} + {{- end }} + {{- end }} + + {{- if eq .Values.provider "ovh" }} + {{- if or .Values.ovh.endpoint .Values.ovh.applicationKey .Values.ovh.applicationSecret .Values.ovh.consumerKey }} + env: + - name: OVH_ENDPOINT + value: {{ .Values.ovh.endpoint | quote }} + - name: OVH_APPLICATION_KEY + value: {{ .Values.ovh.applicationKey | quote }} + - name: OVH_APPLICATION_SECRET + value: {{ .Values.ovh.applicationSecret | quote }} + - name: OVH_CONSUMER_KEY + value: {{ .Values.ovh.consumerKey | quote }} + {{- end }} + {{- end }} + + {{- if eq .Values.provider "exoscale" }} + {{- if or .Values.exoscale.apiKey .Values.exoscale.apiSecret }} + env: + - name: EXOSCALE_API_KEY + value: {{ .Values.exoscale.apiKey | quote }} + - name: EXOSCALE_API_SECRET + value: {{ .Values.exoscale.apiSecret | quote }} + {{- end }} + {{- end }} + + {{- /* extraArgs: merge user-supplied args with provider-specific args (godaddy) */}} + {{- $godaddyArgs := list }} + {{- if eq .Values.provider "godaddy" }} + {{- if .Values.godaddy.apiKey }} + {{- $godaddyArgs = append $godaddyArgs (printf "--godaddy-api-key=%s" .Values.godaddy.apiKey) }} + {{- end }} + {{- if .Values.godaddy.apiSecret }} + {{- $godaddyArgs = append $godaddyArgs (printf "--godaddy-api-secret=%s" .Values.godaddy.apiSecret) }} + {{- end }} + {{- end }} + {{- $allArgs := concat (.Values.extraArgs | default list) $godaddyArgs }} + {{- with $allArgs }} + extraArgs: + {{- toYaml . | nindent 8 }} + {{- end }} + + {{- with (include "cozy-lib.resources.defaultingSanitize" (list .Values.resourcesPreset .Values.resources $)) }} + resources: + {{- . | nindent 8 }} + {{- end }} diff --git a/packages/extra/external-dns/values.schema.json b/packages/extra/external-dns/values.schema.json new file mode 100644 index 00000000..08de0f8e --- /dev/null +++ b/packages/extra/external-dns/values.schema.json @@ -0,0 +1,193 @@ +{ + "title": "Chart Values", + "type": "object", + "properties": { + "provider": { + "description": "DNS provider name.", + "type": "string", + "enum": [ + "cloudflare", + "aws", + "azure", + "google", + "digitalocean", + "linode", + "ovh", + "exoscale", + "godaddy", + "inmemory" + ] + }, + "domainFilters": { + "description": "List of domains this external-dns instance can manage.", + "type": "array", + "default": [], + "items": { + "type": "string" + } + }, + "policy": { + "description": "How DNS records are synchronized.", + "type": "string", + "default": "upsert-only", + "enum": [ + "create-only", + "sync", + "upsert-only" + ] + }, + "extraArgs": { + "description": "Extra arguments for external-dns.", + "type": "array", + "default": [], + "items": { + "type": "string" + } + }, + "gatewayAPI": { + "description": "Enable Gateway API HTTPRoute as a source for DNS records.", + "type": "boolean", + "default": false + }, + "annotationPrefix": { + "description": "Custom annotation prefix for external-dns (useful for running multiple instances).", + "type": "string", + "default": "" + }, + "cloudflare": { + "description": "Cloudflare provider credentials.", + "type": "object", + "default": { + "apiEmail": "", + "apiKey": "", + "apiToken": "", + "proxied": false + }, + "x-kubernetes-preserve-unknown-fields": true + }, + "aws": { + "description": "AWS Route53 provider credentials.", + "type": "object", + "default": { + "accessKeyId": "", + "region": "", + "secretAccessKey": "", + "zoneType": "" + }, + "x-kubernetes-preserve-unknown-fields": true + }, + "azure": { + "description": "Azure DNS provider credentials.", + "type": "object", + "default": { + "aadClientId": "", + "aadClientSecret": "", + "resourceGroup": "", + "subscriptionId": "", + "tenantId": "" + }, + "x-kubernetes-preserve-unknown-fields": true + }, + "google": { + "description": "Google Cloud DNS provider credentials.", + "type": "object", + "default": { + "project": "", + "serviceAccountKey": "" + }, + "x-kubernetes-preserve-unknown-fields": true + }, + "digitalocean": { + "description": "DigitalOcean DNS provider credentials.", + "type": "object", + "default": { + "token": "" + }, + "x-kubernetes-preserve-unknown-fields": true + }, + "linode": { + "description": "Linode DNS provider credentials.", + "type": "object", + "default": { + "token": "" + }, + "x-kubernetes-preserve-unknown-fields": true + }, + "ovh": { + "description": "OVH DNS provider credentials.", + "type": "object", + "default": { + "applicationKey": "", + "applicationSecret": "", + "consumerKey": "", + "endpoint": "" + }, + "x-kubernetes-preserve-unknown-fields": true + }, + "exoscale": { + "description": "Exoscale DNS provider credentials.", + "type": "object", + "default": { + "apiKey": "", + "apiSecret": "" + }, + "x-kubernetes-preserve-unknown-fields": true + }, + "godaddy": { + "description": "GoDaddy DNS provider credentials.", + "type": "object", + "default": { + "apiKey": "", + "apiSecret": "" + }, + "x-kubernetes-preserve-unknown-fields": true + }, + "resources": { + "description": "Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.", + "type": "object", + "default": {}, + "properties": { + "cpu": { + "description": "CPU available to each replica.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "memory": { + "description": "Memory (RAM) available to each replica.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + } + } + }, + "resourcesPreset": { + "description": "Default sizing preset used when `resources` is omitted.", + "type": "string", + "default": "nano", + "enum": [ + "nano", + "micro", + "small", + "medium", + "large", + "xlarge", + "2xlarge" + ] + } + } +} diff --git a/packages/extra/external-dns/values.yaml b/packages/extra/external-dns/values.yaml new file mode 100644 index 00000000..c40d2dff --- /dev/null +++ b/packages/extra/external-dns/values.yaml @@ -0,0 +1,149 @@ +## +## @section Common parameters +## + +## @enum {string} Provider - DNS provider. +## @value cloudflare +## @value aws +## @value azure +## @value google +## @value digitalocean +## @value linode +## @value ovh +## @value exoscale +## @value godaddy +## @value inmemory + +## @param {Provider} provider - DNS provider name. +# provider: + +## @param {[]string} domainFilters - List of domains this external-dns instance can manage. +domainFilters: [] + +## @enum {string} Policy - How DNS records are synchronized. +## @value create-only +## @value sync +## @value upsert-only + +## @param {Policy} policy="upsert-only" - How DNS records are synchronized. +policy: "upsert-only" + +## @param {[]string} extraArgs - Extra arguments for external-dns. +extraArgs: [] + +## @param {bool} gatewayAPI=false - Enable Gateway API HTTPRoute as a source for DNS records. +gatewayAPI: false + +## @param {string} [annotationPrefix] - Custom annotation prefix for external-dns (useful for running multiple instances). +annotationPrefix: "" + +## +## @section Cloudflare +## + +## @param {object} cloudflare - Cloudflare provider credentials. +cloudflare: + apiToken: "" + apiKey: "" + apiEmail: "" + proxied: false + +## +## @section AWS +## + +## @param {object} aws - AWS Route53 provider credentials. +aws: + accessKeyId: "" + secretAccessKey: "" + region: "" + zoneType: "" + +## +## @section Azure +## + +## @param {object} azure - Azure DNS provider credentials. +azure: + tenantId: "" + subscriptionId: "" + resourceGroup: "" + aadClientId: "" + aadClientSecret: "" + +## +## @section Google +## + +## @param {object} google - Google Cloud DNS provider credentials. +google: + project: "" + serviceAccountKey: "" + +## +## @section DigitalOcean +## + +## @param {object} digitalocean - DigitalOcean DNS provider credentials. +digitalocean: + token: "" + +## +## @section Linode +## + +## @param {object} linode - Linode DNS provider credentials. +linode: + token: "" + +## +## @section OVH +## + +## @param {object} ovh - OVH DNS provider credentials. +ovh: + endpoint: "" + applicationKey: "" + applicationSecret: "" + consumerKey: "" + +## +## @section Exoscale +## + +## @param {object} exoscale - Exoscale DNS provider credentials. +exoscale: + apiKey: "" + apiSecret: "" + +## +## @section GoDaddy +## + +## @param {object} godaddy - GoDaddy DNS provider credentials. +godaddy: + apiKey: "" + apiSecret: "" + +## +## @section Resources +## + +## @typedef {struct} Resources - Explicit CPU and memory configuration. +## @field {quantity} [cpu] - CPU available to each replica. +## @field {quantity} [memory] - Memory (RAM) available to each replica. + +## @enum {string} ResourcesPreset - Default sizing preset. +## @value nano +## @value micro +## @value small +## @value medium +## @value large +## @value xlarge +## @value 2xlarge + +## @param {Resources} [resources] - Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. +resources: {} + +## @param {ResourcesPreset} resourcesPreset="nano" - Default sizing preset used when `resources` is omitted. +resourcesPreset: "nano" diff --git a/packages/extra/ingress/Makefile b/packages/extra/ingress/Makefile index 958ce484..65b53c03 100644 --- a/packages/extra/ingress/Makefile +++ b/packages/extra/ingress/Makefile @@ -10,3 +10,6 @@ get-cloudflare-ips: generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md ../../../hack/update-crd.sh + +test: + helm unittest . diff --git a/packages/extra/ingress/README.md b/packages/extra/ingress/README.md index 0e786dfb..c541d8e9 100644 --- a/packages/extra/ingress/README.md +++ b/packages/extra/ingress/README.md @@ -14,3 +14,17 @@ | `resources.memory` | Memory (RAM) available to each replica. | `quantity` | `""` | | `resourcesPreset` | Default sizing preset used when `resources` is omitted. | `string` | `micro` | + +## Exposure mode + +The ingress Service type is driven by the cluster-wide `publishing.exposure` value in the platform chart, not by any key in this package. Two modes exist: + +- `externalIPs` (default) has three rendered shapes: + - Release namespace matches `publishing.ingressName` AND `publishing.externalIPs` is non-empty → Service is `ClusterIP` with `Service.spec.externalIPs` set from that list and `externalTrafficPolicy: Cluster`. + - Release namespace matches `publishing.ingressName` but `publishing.externalIPs` is empty → Service falls back to `type: LoadBalancer` with `externalTrafficPolicy: Local`. + - Release namespace does not match `publishing.ingressName` (non-root tenants) → Service is `type: LoadBalancer` with `externalTrafficPolicy: Local`. + `Service.spec.externalIPs` is deprecated upstream in Kubernetes v1.36 (KEP-5707); plan migration before v1.40. +- `loadBalancer` — Service is `type: LoadBalancer` with `externalTrafficPolicy: Local`, and a `CiliumLoadBalancerIPPool` makes the addresses in `publishing.externalIPs` allocatable via Cilium LB IPAM. Requires `publishing.externalIPs` to contain at least one non-empty address (render fails otherwise) and assumes the addresses are already routed to a cluster node (floating IP / upstream router). See the inline comment on `publishing.exposure` in the platform chart for full caveats, including the note that switching the value on a running cluster causes the ingress Service to be recreated. + +This setting only migrates ingress-nginx away from `Service.spec.externalIPs`. Other cozystack components that use the same deprecated field (e.g. the `vpn` app) must be migrated separately before Kubernetes v1.40 flips the `AllowServiceExternalIPs` feature gate off. + diff --git a/packages/extra/ingress/templates/nginx-ingress.yaml b/packages/extra/ingress/templates/nginx-ingress.yaml index ca50d276..8e1f00fb 100644 --- a/packages/extra/ingress/templates/nginx-ingress.yaml +++ b/packages/extra/ingress/templates/nginx-ingress.yaml @@ -1,5 +1,19 @@ {{- $exposeIngress := (index .Values._cluster "expose-ingress") | default "tenant-root" }} {{- $exposeExternalIPs := (index .Values._cluster "expose-external-ips") | default "" | nospace }} +{{- $exposeMode := (index .Values._cluster "expose-mode") | default "externalIPs" }} +{{- $exposeIPsList := list }} +{{- range splitList "," $exposeExternalIPs }} + {{- $ip := . | trim }} + {{- if $ip }} + {{- $exposeIPsList = append $exposeIPsList $ip }} + {{- end }} +{{- end }} +{{- if not (has $exposeMode (list "externalIPs" "loadBalancer")) }} +{{- fail (printf "unknown publishing.exposure mode %q: must be \"externalIPs\" or \"loadBalancer\"" $exposeMode) }} +{{- end }} +{{- if and (eq $exposeMode "loadBalancer") (eq $exposeIngress .Release.Namespace) (not $exposeIPsList) }} +{{- fail "publishing.exposure=loadBalancer requires publishing.externalIPs to contain at least one non-empty address: the CiliumLoadBalancerIPPool has nothing to advertise and the Service would stay in ." }} +{{- end }} apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: @@ -41,9 +55,12 @@ spec: enabled: false {{- end }} service: - {{- if and (eq $exposeIngress .Release.Namespace) $exposeExternalIPs }} + {{- if and (eq $exposeIngress .Release.Namespace) (eq $exposeMode "loadBalancer") }} + type: LoadBalancer + externalTrafficPolicy: Local + {{- else if and (eq $exposeIngress .Release.Namespace) $exposeIPsList }} externalIPs: - {{- toYaml (splitList "," $exposeExternalIPs) | nindent 12 }} + {{- toYaml $exposeIPsList | nindent 12 }} type: ClusterIP externalTrafficPolicy: Cluster {{- else }} diff --git a/packages/extra/ingress/tests/exposure_test.yaml b/packages/extra/ingress/tests/exposure_test.yaml new file mode 100644 index 00000000..1c76b3da --- /dev/null +++ b/packages/extra/ingress/tests/exposure_test.yaml @@ -0,0 +1,165 @@ +suite: ingress exposure modes +templates: + - templates/nginx-ingress.yaml + +release: + name: ingress + namespace: tenant-root + +tests: + - it: default exposure (externalIPs) renders ClusterIP Service with spec.externalIPs + set: + _cluster: + expose-ingress: tenant-root + expose-external-ips: "192.0.2.10,192.0.2.11" + asserts: + - template: templates/nginx-ingress.yaml + equal: + path: spec.values.ingress-nginx.controller.service.type + value: ClusterIP + - template: templates/nginx-ingress.yaml + equal: + path: spec.values.ingress-nginx.controller.service.externalTrafficPolicy + value: Cluster + - template: templates/nginx-ingress.yaml + equal: + path: spec.values.ingress-nginx.controller.service.externalIPs + value: + - 192.0.2.10 + - 192.0.2.11 + - template: templates/nginx-ingress.yaml + notExists: + path: spec.values.ingress-nginx.controller.service.labels + + - it: legacy config without expose-mode falls back to externalIPs behavior + set: + _cluster: + expose-ingress: tenant-root + expose-external-ips: "192.0.2.10" + asserts: + - template: templates/nginx-ingress.yaml + equal: + path: spec.values.ingress-nginx.controller.service.type + value: ClusterIP + + - it: externalIPs mode in a namespace other than publishing.ingressName renders LoadBalancer fallback without externalIPs + set: + _cluster: + expose-ingress: tenant-root + expose-external-ips: "192.0.2.10" + release: + namespace: tenant-other + asserts: + - template: templates/nginx-ingress.yaml + equal: + path: spec.values.ingress-nginx.controller.service.type + value: LoadBalancer + - template: templates/nginx-ingress.yaml + equal: + path: spec.values.ingress-nginx.controller.service.externalTrafficPolicy + value: Local + - template: templates/nginx-ingress.yaml + notExists: + path: spec.values.ingress-nginx.controller.service.externalIPs + + - it: loadBalancer mode renders LoadBalancer Service without externalIPs on the Service + set: + _cluster: + expose-ingress: tenant-root + expose-external-ips: "192.0.2.10,192.0.2.11" + expose-mode: loadBalancer + asserts: + - template: templates/nginx-ingress.yaml + equal: + path: spec.values.ingress-nginx.controller.service.type + value: LoadBalancer + - template: templates/nginx-ingress.yaml + equal: + path: spec.values.ingress-nginx.controller.service.externalTrafficPolicy + value: Local + - template: templates/nginx-ingress.yaml + notExists: + path: spec.values.ingress-nginx.controller.service.labels + - template: templates/nginx-ingress.yaml + notExists: + path: spec.values.ingress-nginx.controller.service.externalIPs + + - it: loadBalancer mode without externalIPs fails chart render with explicit message + set: + _cluster: + expose-ingress: tenant-root + expose-external-ips: "" + expose-mode: loadBalancer + asserts: + - template: templates/nginx-ingress.yaml + failedTemplate: + errorMessage: "publishing.exposure=loadBalancer requires publishing.externalIPs to contain at least one non-empty address: the CiliumLoadBalancerIPPool has nothing to advertise and the Service would stay in ." + + - it: unknown exposure mode is rejected with a clear error (case-sensitive enum) + set: + _cluster: + expose-ingress: tenant-root + expose-external-ips: "192.0.2.10" + expose-mode: LoadBalancer + asserts: + - template: templates/nginx-ingress.yaml + failedTemplate: + errorMessage: "unknown publishing.exposure mode \"LoadBalancer\": must be \"externalIPs\" or \"loadBalancer\"" + + - it: another typo in exposure mode also fails + set: + _cluster: + expose-ingress: tenant-root + expose-external-ips: "192.0.2.10" + expose-mode: loadbalancer + asserts: + - template: templates/nginx-ingress.yaml + failedTemplate: + errorMessage: "unknown publishing.exposure mode \"loadbalancer\": must be \"externalIPs\" or \"loadBalancer\"" + + - it: loadBalancer mode in a namespace other than publishing.ingressName falls back to LoadBalancer Service without pool + set: + _cluster: + expose-ingress: tenant-root + expose-external-ips: "192.0.2.10" + expose-mode: loadBalancer + release: + namespace: tenant-other + asserts: + - template: templates/nginx-ingress.yaml + equal: + path: spec.values.ingress-nginx.controller.service.type + value: LoadBalancer + - template: templates/nginx-ingress.yaml + equal: + path: spec.values.ingress-nginx.controller.service.externalTrafficPolicy + value: Local + - template: templates/nginx-ingress.yaml + notExists: + path: spec.values.ingress-nginx.controller.service.labels + - template: templates/nginx-ingress.yaml + notExists: + path: spec.values.ingress-nginx.controller.service.externalIPs + + - it: loadBalancer mode with only-empty externalIPs fails chart render (comma-only input) + set: + _cluster: + expose-ingress: tenant-root + expose-external-ips: ",," + expose-mode: loadBalancer + asserts: + - template: templates/nginx-ingress.yaml + failedTemplate: + errorMessage: "publishing.exposure=loadBalancer requires publishing.externalIPs to contain at least one non-empty address: the CiliumLoadBalancerIPPool has nothing to advertise and the Service would stay in ." + + - it: externalIPs mode also filters out empty entries (trailing comma) + set: + _cluster: + expose-ingress: tenant-root + expose-external-ips: "192.0.2.10," + asserts: + - template: templates/nginx-ingress.yaml + equal: + path: spec.values.ingress-nginx.controller.service.externalIPs + value: + - 192.0.2.10 diff --git a/packages/extra/ingress/values.schema.json b/packages/extra/ingress/values.schema.json index 650a5824..7a53a197 100644 --- a/packages/extra/ingress/values.schema.json +++ b/packages/extra/ingress/values.schema.json @@ -2,16 +2,24 @@ "title": "Chart Values", "type": "object", "properties": { - "cloudflareProxy": { - "description": "Restoring original visitor IPs when Cloudflare proxied is enabled.", - "type": "boolean", - "default": false - }, "replicas": { "description": "Number of ingress-nginx replicas.", "type": "integer", "default": 2 }, + "whitelist": { + "description": "List of client networks.", + "type": "array", + "default": [], + "items": { + "type": "string" + } + }, + "cloudflareProxy": { + "description": "Restoring original visitor IPs when Cloudflare proxied is enabled.", + "type": "boolean", + "default": false + }, "resources": { "description": "Explicit CPU and memory configuration for each ingress-nginx replica. When omitted, the preset defined in `resourcesPreset` is applied.", "type": "object", @@ -58,14 +66,6 @@ "xlarge", "2xlarge" ] - }, - "whitelist": { - "description": "List of client networks.", - "type": "array", - "default": [], - "items": { - "type": "string" - } } } -} \ No newline at end of file +} diff --git a/packages/extra/monitoring/templates/dashboard-resourcemap.yaml b/packages/extra/monitoring/templates/dashboard-resourcemap.yaml index 894a11d4..5a5a3def 100644 --- a/packages/extra/monitoring/templates/dashboard-resourcemap.yaml +++ b/packages/extra/monitoring/templates/dashboard-resourcemap.yaml @@ -42,7 +42,9 @@ rules: - {{ .name }}-vminsert {{- end }} {{- range .Values.logsStorages }} - - {{ $.Release.Name }}-vlogs-{{ .name }} + - {{ .name }}-vlstorage + - {{ .name }}-vlselect + - {{ .name }}-vlinsert {{- end }} {{- range .Values.metricsStorages }} - vmalert-{{ .name }} diff --git a/packages/extra/monitoring/templates/workloadmonitors.yaml b/packages/extra/monitoring/templates/workloadmonitors.yaml index 6dbfa8c1..5d803351 100644 --- a/packages/extra/monitoring/templates/workloadmonitors.yaml +++ b/packages/extra/monitoring/templates/workloadmonitors.yaml @@ -67,16 +67,46 @@ spec: apiVersion: cozystack.io/v1alpha1 kind: WorkloadMonitor metadata: - name: vlogs-{{ .name }} + name: {{ .name }}-vlstorage spec: - replicas: 1 + replicas: 2 minReplicas: 1 kind: monitoring - type: vlogs + type: vlstorage selector: app.kubernetes.io/component: monitoring app.kubernetes.io/instance: {{ .name }} - app.kubernetes.io/name: vlogs + app.kubernetes.io/name: vlstorage + version: {{ $.Chart.Version }} +--- +apiVersion: cozystack.io/v1alpha1 +kind: WorkloadMonitor +metadata: + name: {{ .name }}-vlselect +spec: + replicas: 2 + minReplicas: 1 + kind: monitoring + type: vlselect + selector: + app.kubernetes.io/component: monitoring + app.kubernetes.io/instance: {{ .name }} + app.kubernetes.io/name: vlselect + version: {{ $.Chart.Version }} +--- +apiVersion: cozystack.io/v1alpha1 +kind: WorkloadMonitor +metadata: + name: {{ .name }}-vlinsert +spec: + replicas: 2 + minReplicas: 1 + kind: monitoring + type: vlinsert + selector: + app.kubernetes.io/component: monitoring + app.kubernetes.io/instance: {{ .name }} + app.kubernetes.io/name: vlinsert version: {{ $.Chart.Version }} {{- end }} --- diff --git a/packages/extra/monitoring/values.schema.json b/packages/extra/monitoring/values.schema.json index 0a6f0e64..4bc6906e 100644 --- a/packages/extra/monitoring/values.schema.json +++ b/packages/extra/monitoring/values.schema.json @@ -2,302 +2,11 @@ "title": "Chart Values", "type": "object", "properties": { - "alerta": { - "description": "Configuration for the Alerta service.", - "type": "object", - "default": {}, - "properties": { - "alerts": { - "description": "Alert routing configuration.", - "type": "object", - "default": {}, - "properties": { - "slack": { - "description": "Configuration for Slack alerts.", - "type": "object", - "default": {}, - "required": [ - "url" - ], - "properties": { - "disabledSeverity": { - "description": "List of severities without alerts (e.g. [\"informational\",\"warning\"]).", - "type": "array", - "default": [], - "items": { - "type": "string" - } - }, - "url": { - "description": "Configuration uri for Slack alerts.", - "type": "string", - "default": "" - } - } - }, - "telegram": { - "description": "Configuration for Telegram alerts.", - "type": "object", - "default": {}, - "required": [ - "chatID", - "token" - ], - "properties": { - "chatID": { - "description": "Telegram chat ID(s), separated by commas.", - "type": "string", - "default": "" - }, - "disabledSeverity": { - "description": "List of severities without alerts (e.g. [\"informational\",\"warning\"]).", - "type": "array", - "default": [], - "items": { - "type": "string" - } - }, - "token": { - "description": "Telegram bot token.", - "type": "string", - "default": "" - } - } - } - } - }, - "resources": { - "description": "Resource configuration.", - "type": "object", - "default": {}, - "properties": { - "limits": { - "description": "Resource limits.", - "type": "object", - "default": {}, - "properties": { - "cpu": { - "description": "CPU limit.", - "default": 1, - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - }, - "memory": { - "description": "Memory limit.", - "default": "1Gi", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - } - } - }, - "requests": { - "description": "Resource requests.", - "type": "object", - "default": {}, - "properties": { - "cpu": { - "description": "CPU request.", - "default": "100m", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - }, - "memory": { - "description": "Memory request.", - "default": "256Mi", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - } - } - } - } - }, - "storage": { - "description": "Persistent volume size for the database.", - "type": "string", - "default": "10Gi" - }, - "storageClassName": { - "description": "StorageClass used for the database.", - "type": "string", - "default": "" - } - } - }, - "grafana": { - "description": "Configuration for Grafana.", - "type": "object", - "default": {}, - "properties": { - "db": { - "description": "Database configuration.", - "type": "object", - "default": {}, - "properties": { - "size": { - "description": "Persistent volume size for the database.", - "type": "string", - "default": "10Gi" - } - } - }, - "resources": { - "description": "Resource configuration.", - "type": "object", - "default": {}, - "properties": { - "limits": { - "description": "Resource limits.", - "type": "object", - "default": {}, - "properties": { - "cpu": { - "description": "CPU limit.", - "default": 1, - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - }, - "memory": { - "description": "Memory limit.", - "default": "1Gi", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - } - } - }, - "requests": { - "description": "Resource requests.", - "type": "object", - "default": {}, - "properties": { - "cpu": { - "description": "CPU request.", - "default": "100m", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - }, - "memory": { - "description": "Memory request.", - "default": "256Mi", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - } - } - } - } - } - } - }, "host": { "description": "The hostname used to access Grafana externally (defaults to 'grafana' subdomain for the tenant host).", "type": "string", "default": "" }, - "logsStorages": { - "description": "Configuration of logs storage instances.", - "type": "array", - "default": [ - { - "name": "generic", - "retentionPeriod": "1", - "storage": "10Gi", - "storageClassName": "replicated" - } - ], - "items": { - "type": "object", - "required": [ - "name", - "retentionPeriod", - "storage", - "storageClassName" - ], - "properties": { - "name": { - "description": "Name of the storage instance.", - "type": "string" - }, - "retentionPeriod": { - "description": "Retention period for logs.", - "type": "string", - "default": "1" - }, - "storage": { - "description": "Persistent volume size.", - "type": "string", - "default": "10Gi" - }, - "storageClassName": { - "description": "StorageClass used to store the data.", - "type": "string", - "default": "replicated" - } - } - } - }, "metricsStorages": { "description": "Configuration of metrics storage instances.", "type": "array", @@ -572,6 +281,297 @@ } } }, + "logsStorages": { + "description": "Configuration of logs storage instances.", + "type": "array", + "default": [ + { + "name": "generic", + "retentionPeriod": "1", + "storage": "10Gi", + "storageClassName": "replicated" + } + ], + "items": { + "type": "object", + "required": [ + "name", + "retentionPeriod", + "storage", + "storageClassName" + ], + "properties": { + "name": { + "description": "Name of the storage instance.", + "type": "string" + }, + "retentionPeriod": { + "description": "Retention period for logs.", + "type": "string", + "default": "1" + }, + "storage": { + "description": "Persistent volume size.", + "type": "string", + "default": "10Gi" + }, + "storageClassName": { + "description": "StorageClass used to store the data.", + "type": "string", + "default": "replicated" + } + } + } + }, + "alerta": { + "description": "Configuration for the Alerta service.", + "type": "object", + "default": {}, + "properties": { + "alerts": { + "description": "Alert routing configuration.", + "type": "object", + "default": {}, + "properties": { + "slack": { + "description": "Configuration for Slack alerts.", + "type": "object", + "default": {}, + "required": [ + "url" + ], + "properties": { + "disabledSeverity": { + "description": "List of severities without alerts (e.g. [\"informational\",\"warning\"]).", + "type": "array", + "default": [], + "items": { + "type": "string" + } + }, + "url": { + "description": "Configuration uri for Slack alerts.", + "type": "string", + "default": "" + } + } + }, + "telegram": { + "description": "Configuration for Telegram alerts.", + "type": "object", + "default": {}, + "required": [ + "chatID", + "token" + ], + "properties": { + "chatID": { + "description": "Telegram chat ID(s), separated by commas.", + "type": "string", + "default": "" + }, + "disabledSeverity": { + "description": "List of severities without alerts (e.g. [\"informational\",\"warning\"]).", + "type": "array", + "default": [], + "items": { + "type": "string" + } + }, + "token": { + "description": "Telegram bot token.", + "type": "string", + "default": "" + } + } + } + } + }, + "resources": { + "description": "Resource configuration.", + "type": "object", + "default": {}, + "properties": { + "limits": { + "description": "Resource limits.", + "type": "object", + "default": {}, + "properties": { + "cpu": { + "description": "CPU limit.", + "default": 1, + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "memory": { + "description": "Memory limit.", + "default": "1Gi", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + } + } + }, + "requests": { + "description": "Resource requests.", + "type": "object", + "default": {}, + "properties": { + "cpu": { + "description": "CPU request.", + "default": "100m", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "memory": { + "description": "Memory request.", + "default": "256Mi", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + } + } + } + } + }, + "storage": { + "description": "Persistent volume size for the database.", + "type": "string", + "default": "10Gi" + }, + "storageClassName": { + "description": "StorageClass used for the database.", + "type": "string", + "default": "" + } + } + }, + "grafana": { + "description": "Configuration for Grafana.", + "type": "object", + "default": {}, + "properties": { + "db": { + "description": "Database configuration.", + "type": "object", + "default": {}, + "properties": { + "size": { + "description": "Persistent volume size for the database.", + "type": "string", + "default": "10Gi" + } + } + }, + "resources": { + "description": "Resource configuration.", + "type": "object", + "default": {}, + "properties": { + "limits": { + "description": "Resource limits.", + "type": "object", + "default": {}, + "properties": { + "cpu": { + "description": "CPU limit.", + "default": 1, + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "memory": { + "description": "Memory limit.", + "default": "1Gi", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + } + } + }, + "requests": { + "description": "Resource requests.", + "type": "object", + "default": {}, + "properties": { + "cpu": { + "description": "CPU request.", + "default": "100m", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "memory": { + "description": "Memory request.", + "default": "256Mi", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + } + } + } + } + } + } + }, "vmagent": { "description": "Configuration for VictoriaMetrics Agent.", "type": "object", @@ -579,12 +579,14 @@ "properties": { "externalLabels": { "description": "External labels applied to all metrics.", + "type": "object", "default": { "cluster": "cozystack" } }, "remoteWrite": { "description": "Remote write configuration.", + "type": "object", "default": { "urls": [ "http://vminsert-shortterm:8480/insert/0/prometheus", @@ -595,4 +597,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/extra/monitoring/values.yaml b/packages/extra/monitoring/values.yaml index ab32b1b2..c521f389 100644 --- a/packages/extra/monitoring/values.yaml +++ b/packages/extra/monitoring/values.yaml @@ -178,3 +178,7 @@ vmagent: urls: - http://vminsert-shortterm:8480/insert/0/prometheus - http://vminsert-longterm:8480/insert/0/prometheus + ## inlineScrapeConfig: | + ## - job_name: "custom" + ## static_configs: + ## - targets: ["my-service:9090"] diff --git a/packages/extra/seaweedfs/README.md b/packages/extra/seaweedfs/README.md index a6ad408f..62bb9bf2 100644 --- a/packages/extra/seaweedfs/README.md +++ b/packages/extra/seaweedfs/README.md @@ -1,4 +1,4 @@ -# Managed NATS Service +# Managed SeaweedFS Service ## Parameters @@ -13,46 +13,68 @@ ### SeaweedFS Components Configuration -| Name | Description | Type | Value | -| ----------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------- | ------- | -| `db` | Database configuration. | `object` | `{}` | -| `db.replicas` | Number of database replicas. | `int` | `2` | -| `db.size` | Persistent Volume size. | `quantity` | `10Gi` | -| `db.storageClass` | StorageClass used to store the data. | `string` | `""` | -| `db.resources` | Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` | -| `db.resources.cpu` | Number of CPU cores allocated. | `quantity` | `""` | -| `db.resources.memory` | Amount of memory allocated. | `quantity` | `""` | -| `db.resourcesPreset` | Default sizing preset used when `resources` is omitted. | `string` | `small` | -| `master` | Master service configuration. | `object` | `{}` | -| `master.replicas` | Number of master replicas. | `int` | `3` | -| `master.resources` | Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` | -| `master.resources.cpu` | Number of CPU cores allocated. | `quantity` | `""` | -| `master.resources.memory` | Amount of memory allocated. | `quantity` | `""` | -| `master.resourcesPreset` | Default sizing preset used when `resources` is omitted. | `string` | `small` | -| `filer` | Filer service configuration. | `object` | `{}` | -| `filer.replicas` | Number of filer replicas. | `int` | `2` | -| `filer.resources` | Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` | -| `filer.resources.cpu` | Number of CPU cores allocated. | `quantity` | `""` | -| `filer.resources.memory` | Amount of memory allocated. | `quantity` | `""` | -| `filer.resourcesPreset` | Default sizing preset used when `resources` is omitted. | `string` | `small` | -| `filer.grpcHost` | The hostname used to expose or access the filer service externally. | `string` | `""` | -| `filer.grpcPort` | The port used to access the filer service externally. | `int` | `443` | -| `filer.whitelist` | A list of IP addresses or CIDR ranges that are allowed to access the filer service. | `[]string` | `[]` | -| `volume` | Volume service configuration. | `object` | `{}` | -| `volume.replicas` | Number of volume replicas. | `int` | `2` | -| `volume.size` | Persistent Volume size. | `quantity` | `10Gi` | -| `volume.storageClass` | StorageClass used to store the data. | `string` | `""` | -| `volume.resources` | Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` | -| `volume.resources.cpu` | Number of CPU cores allocated. | `quantity` | `""` | -| `volume.resources.memory` | Amount of memory allocated. | `quantity` | `""` | -| `volume.resourcesPreset` | Default sizing preset used when `resources` is omitted. | `string` | `small` | -| `volume.zones` | A map of zones for MultiZone topology. Each zone can have its own number of replicas and size. | `map[string]object` | `{}` | -| `volume.zones[name].replicas` | Number of replicas in the zone. | `int` | `0` | -| `volume.zones[name].size` | Zone storage size. | `quantity` | `""` | -| `s3` | S3 service configuration. | `object` | `{}` | -| `s3.replicas` | Number of S3 replicas. | `int` | `2` | -| `s3.resources` | Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` | -| `s3.resources.cpu` | Number of CPU cores allocated. | `quantity` | `""` | -| `s3.resources.memory` | Amount of memory allocated. | `quantity` | `""` | -| `s3.resourcesPreset` | Default sizing preset used when `resources` is omitted. | `string` | `small` | +| Name | Description | Type | Value | +| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ------------------- | ------- | +| `db` | Database configuration. | `object` | `{}` | +| `db.replicas` | Number of database replicas. | `int` | `2` | +| `db.size` | Persistent Volume size. | `quantity` | `10Gi` | +| `db.storageClass` | StorageClass used to store the data. | `string` | `""` | +| `db.resources` | Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` | +| `db.resources.cpu` | Number of CPU cores allocated. | `quantity` | `""` | +| `db.resources.memory` | Amount of memory allocated. | `quantity` | `""` | +| `db.resourcesPreset` | Default sizing preset used when `resources` is omitted. | `string` | `small` | +| `master` | Master service configuration. | `object` | `{}` | +| `master.replicas` | Number of master replicas. | `int` | `3` | +| `master.resources` | Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` | +| `master.resources.cpu` | Number of CPU cores allocated. | `quantity` | `""` | +| `master.resources.memory` | Amount of memory allocated. | `quantity` | `""` | +| `master.resourcesPreset` | Default sizing preset used when `resources` is omitted. | `string` | `small` | +| `filer` | Filer service configuration. | `object` | `{}` | +| `filer.replicas` | Number of filer replicas. | `int` | `2` | +| `filer.resources` | Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` | +| `filer.resources.cpu` | Number of CPU cores allocated. | `quantity` | `""` | +| `filer.resources.memory` | Amount of memory allocated. | `quantity` | `""` | +| `filer.resourcesPreset` | Default sizing preset used when `resources` is omitted. | `string` | `small` | +| `filer.grpcHost` | The hostname used to expose or access the filer service externally. | `string` | `""` | +| `filer.grpcPort` | The port used to access the filer service externally. | `int` | `443` | +| `filer.whitelist` | A list of IP addresses or CIDR ranges that are allowed to access the filer service. | `[]string` | `[]` | +| `volume` | Volume service configuration. | `object` | `{}` | +| `volume.replicas` | Number of volume replicas. | `int` | `2` | +| `volume.size` | Persistent Volume size. | `quantity` | `10Gi` | +| `volume.storageClass` | StorageClass used to store the data. | `string` | `""` | +| `volume.diskType` | SeaweedFS disk type tag for the default volume servers (e.g., "hdd", "ssd"). | `string` | `""` | +| `volume.resources` | Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` | +| `volume.resources.cpu` | Number of CPU cores allocated. | `quantity` | `""` | +| `volume.resources.memory` | Amount of memory allocated. | `quantity` | `""` | +| `volume.resourcesPreset` | Default sizing preset used when `resources` is omitted. | `string` | `small` | +| `volume.zones` | A map of zones for MultiZone topology. Each zone can have its own number of replicas and size. | `map[string]object` | `{}` | +| `volume.zones[name].replicas` | Number of replicas in the zone. | `int` | `0` | +| `volume.zones[name].size` | Zone storage size. | `quantity` | `""` | +| `volume.zones[name].dataCenter` | SeaweedFS data center name for this zone. Defaults to the zone name. | `string` | `""` | +| `volume.zones[name].nodeSelector` | YAML nodeSelector for this zone (default: topology.kubernetes.io/zone: ). | `string` | `""` | +| `volume.zones[name].storageClass` | StorageClass used to store zone data. Defaults to volume.storageClass. | `string` | `""` | +| `volume.zones[name].pools` | A map of storage pools for this zone. Each pool creates a separate Volume StatefulSet per zone. | `map[string]object` | `{}` | +| `volume.zones[name].pools[name].diskType` | SeaweedFS disk type tag (e.g., "ssd", "hdd", "nvme"). | `string` | `""` | +| `volume.zones[name].pools[name].replicas` | Number of volume replicas. Defaults to volume.replicas (Simple) or zone.replicas/volume.replicas (MultiZone). | `int` | `0` | +| `volume.zones[name].pools[name].size` | Persistent Volume size. Defaults to volume.size (Simple) or zone.size/volume.size (MultiZone). | `quantity` | `""` | +| `volume.zones[name].pools[name].storageClass` | Kubernetes StorageClass for the pool. Defaults to volume.storageClass. | `string` | `""` | +| `volume.zones[name].pools[name].resources` | Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` | +| `volume.zones[name].pools[name].resources.cpu` | Number of CPU cores allocated. | `quantity` | `""` | +| `volume.zones[name].pools[name].resources.memory` | Amount of memory allocated. | `quantity` | `""` | +| `volume.zones[name].pools[name].resourcesPreset` | Default sizing preset used when `resources` is omitted. Defaults to volume.resourcesPreset. | `string` | `{}` | +| `volume.pools` | A map of storage pools. Each pool creates a separate Volume StatefulSet with its own disk type. | `map[string]object` | `{}` | +| `volume.pools[name].diskType` | SeaweedFS disk type tag (e.g., "ssd", "hdd", "nvme"). | `string` | `""` | +| `volume.pools[name].replicas` | Number of volume replicas. Defaults to volume.replicas (Simple) or zone.replicas/volume.replicas (MultiZone). | `int` | `0` | +| `volume.pools[name].size` | Persistent Volume size. Defaults to volume.size (Simple) or zone.size/volume.size (MultiZone). | `quantity` | `""` | +| `volume.pools[name].storageClass` | Kubernetes StorageClass for the pool. Defaults to volume.storageClass. | `string` | `""` | +| `volume.pools[name].resources` | Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` | +| `volume.pools[name].resources.cpu` | Number of CPU cores allocated. | `quantity` | `""` | +| `volume.pools[name].resources.memory` | Amount of memory allocated. | `quantity` | `""` | +| `volume.pools[name].resourcesPreset` | Default sizing preset used when `resources` is omitted. Defaults to volume.resourcesPreset. | `string` | `{}` | +| `s3` | S3 service configuration. | `object` | `{}` | +| `s3.replicas` | Number of S3 replicas. | `int` | `2` | +| `s3.resources` | Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` | +| `s3.resources.cpu` | Number of CPU cores allocated. | `quantity` | `""` | +| `s3.resources.memory` | Amount of memory allocated. | `quantity` | `""` | +| `s3.resourcesPreset` | Default sizing preset used when `resources` is omitted. | `string` | `small` | diff --git a/packages/extra/seaweedfs/images/objectstorage-sidecar.tag b/packages/extra/seaweedfs/images/objectstorage-sidecar.tag index 9657b956..9b413da5 100644 --- a/packages/extra/seaweedfs/images/objectstorage-sidecar.tag +++ b/packages/extra/seaweedfs/images/objectstorage-sidecar.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.0.0-beta.6@sha256:235b194a531b70e266a10ef78d2955d19f5b659513f23d8b3cfbbc0dff7fc1c0 +ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.3.0@sha256:5bcccbdb13979a16cee535eb5fbcdf0d87973689010a10b7b25e55c5be3edaa6 diff --git a/packages/extra/seaweedfs/images/seaweedfs-cosi-driver.tag b/packages/extra/seaweedfs/images/seaweedfs-cosi-driver.tag index f07c125d..52494779 100644 --- a/packages/extra/seaweedfs/images/seaweedfs-cosi-driver.tag +++ b/packages/extra/seaweedfs/images/seaweedfs-cosi-driver.tag @@ -1 +1 @@ -ghcr.io/seaweedfs/seaweedfs-cosi-driver:v0.2.0 +ghcr.io/seaweedfs/seaweedfs-cosi-driver:v0.3.0 diff --git a/packages/extra/seaweedfs/templates/client/cosi-bucket-class.yaml b/packages/extra/seaweedfs/templates/client/cosi-bucket-class.yaml index 76eda0dc..ce049b31 100644 --- a/packages/extra/seaweedfs/templates/client/cosi-bucket-class.yaml +++ b/packages/extra/seaweedfs/templates/client/cosi-bucket-class.yaml @@ -7,10 +7,32 @@ metadata: driverName: {{ .Release.Namespace }}.seaweedfs.objectstorage.k8s.io deletionPolicy: Delete --- +kind: BucketClass +apiVersion: objectstorage.k8s.io/v1alpha1 +metadata: + name: {{ .Release.Namespace }}-lock +driverName: {{ .Release.Namespace }}.seaweedfs.objectstorage.k8s.io +deletionPolicy: Retain +parameters: + objectLockEnabled: "true" + objectLockRetentionMode: "COMPLIANCE" + objectLockRetentionDays: "365" +--- kind: BucketAccessClass apiVersion: objectstorage.k8s.io/v1alpha1 metadata: name: {{ .Release.Namespace }} driverName: {{ .Release.Namespace }}.seaweedfs.objectstorage.k8s.io authenticationType: KEY +parameters: + accessPolicy: readwrite +--- +kind: BucketAccessClass +apiVersion: objectstorage.k8s.io/v1alpha1 +metadata: + name: {{ .Release.Namespace }}-readonly +driverName: {{ .Release.Namespace }}.seaweedfs.objectstorage.k8s.io +authenticationType: KEY +parameters: + accessPolicy: readonly {{- end }} diff --git a/packages/extra/seaweedfs/templates/dashboard-resourcemap.yaml b/packages/extra/seaweedfs/templates/dashboard-resourcemap.yaml index ec8860b7..dfd408da 100644 --- a/packages/extra/seaweedfs/templates/dashboard-resourcemap.yaml +++ b/packages/extra/seaweedfs/templates/dashboard-resourcemap.yaml @@ -25,8 +25,21 @@ rules: resourceNames: - {{ $.Release.Name }}-master - {{ $.Release.Name }}-filer - - {{ $.Release.Name }}-volume - {{ $.Release.Name }}-db + - {{ $.Release.Name }}-s3 + {{- if eq .Values.topology "Simple" }} + - {{ $.Release.Name }}-volume + {{- range $poolName, $pool := .Values.volume.pools }} + - {{ $.Release.Name }}-volume-{{ $poolName }} + {{- end }} + {{- else if eq .Values.topology "MultiZone" }} + {{- range $zoneName, $zone := .Values.volume.zones }} + - {{ $.Release.Name }}-volume-{{ $zoneName }} + {{- range $poolName, $pool := (dig "pools" dict $zone) }} + - {{ $.Release.Name }}-volume-{{ $zoneName }}-{{ $poolName }} + {{- end }} + {{- end }} + {{- end }} verbs: ["get", "list", "watch"] {{- end }} diff --git a/packages/extra/seaweedfs/templates/seaweedfs.yaml b/packages/extra/seaweedfs/templates/seaweedfs.yaml index b04318bb..2f5720ca 100644 --- a/packages/extra/seaweedfs/templates/seaweedfs.yaml +++ b/packages/extra/seaweedfs/templates/seaweedfs.yaml @@ -16,6 +16,65 @@ {{- fail "replicationFactor must be less than or equal to the number of zones defined in .Values.volume.zones." }} {{- end }} {{- end }} +{{- if and (eq .Values.topology "Client") (gt (len .Values.volume.pools) 0) }} +{{- fail "volume.pools is not supported with Client topology." }} +{{- end }} +{{- if and (eq .Values.topology "MultiZone") (gt (len .Values.volume.pools) 0) }} +{{- fail "volume.pools is not supported with MultiZone topology. Use volume.zones[name].pools instead." }} +{{- end }} +{{- if and .Values.volume.diskType (not (regexMatch "^[a-z0-9]+$" .Values.volume.diskType)) }} +{{- fail (printf "volume.diskType must be lowercase alphanumeric (got: %s)." .Values.volume.diskType) }} +{{- end }} + +{{- /* Collect and validate all pools from volume.pools and zones[].pools */ -}} +{{- $allPools := dict }} +{{- range $poolName, $pool := .Values.volume.pools }} +{{- if not (regexMatch "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$" $poolName) }} +{{- fail (printf "volume.pools key '%s' must be a valid DNS label (lowercase alphanumeric and hyphens, no dots)." $poolName) }} +{{- end }} +{{- if or (hasSuffix "-lock" $poolName) (hasSuffix "-readonly" $poolName) }} +{{- fail (printf "volume.pools key '%s' must not end with '-lock' or '-readonly' (reserved suffixes for COSI resources)." $poolName) }} +{{- end }} +{{- if not $pool.diskType }} +{{- fail (printf "volume.pools.%s.diskType is required." $poolName) }} +{{- end }} +{{- if not (regexMatch "^[a-z0-9]+$" $pool.diskType) }} +{{- fail (printf "volume.pools.%s.diskType must be lowercase alphanumeric (got: %s)." $poolName $pool.diskType) }} +{{- end }} +{{- if and $.Values.volume.diskType (eq $pool.diskType $.Values.volume.diskType) }} +{{- fail (printf "volume.pools.%s.diskType '%s' must differ from volume.diskType." $poolName $pool.diskType) }} +{{- end }} +{{- $_ := set $allPools $poolName $pool.diskType }} +{{- end }} +{{- if eq .Values.topology "MultiZone" }} +{{- range $zoneName, $zone := .Values.volume.zones }} +{{- range $poolName, $pool := (dig "pools" dict $zone) }} +{{- if not (regexMatch "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$" $poolName) }} +{{- fail (printf "volume.zones.%s.pools key '%s' must be a valid DNS label." $zoneName $poolName) }} +{{- end }} +{{- if or (hasSuffix "-lock" $poolName) (hasSuffix "-readonly" $poolName) }} +{{- fail (printf "volume.zones.%s.pools key '%s' must not end with '-lock' or '-readonly' (reserved suffixes for COSI resources)." $zoneName $poolName) }} +{{- end }} +{{- if not $pool.diskType }} +{{- fail (printf "volume.zones.%s.pools.%s.diskType is required." $zoneName $poolName) }} +{{- end }} +{{- if not (regexMatch "^[a-z0-9]+$" $pool.diskType) }} +{{- fail (printf "volume.zones.%s.pools.%s.diskType must be lowercase alphanumeric (got: %s)." $zoneName $poolName $pool.diskType) }} +{{- end }} +{{- if and $.Values.volume.diskType (eq $pool.diskType $.Values.volume.diskType) }} +{{- fail (printf "volume.zones.%s.pools.%s.diskType '%s' must differ from volume.diskType." $zoneName $poolName $pool.diskType) }} +{{- end }} +{{- if and (hasKey $allPools $poolName) (ne (get $allPools $poolName) $pool.diskType) }} +{{- fail (printf "Pool '%s' has inconsistent diskType across zones (expected '%s', got '%s' in zone '%s')." $poolName (get $allPools $poolName) $pool.diskType $zoneName) }} +{{- end }} +{{- $_ := set $allPools $poolName $pool.diskType }} +{{- $composedName := printf "%s-%s" $zoneName $poolName }} +{{- if hasKey $.Values.volume.zones $composedName }} +{{- fail (printf "Composed volume name '%s' (from zone '%s' and pool '%s') collides with an existing zone name." $composedName $zoneName $poolName) }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} {{- $detectedTopology := "Unknown" }} {{- $configMap := lookup "v1" "ConfigMap" .Release.Namespace (printf "%s-deployed-topology" .Release.Name) }} @@ -36,6 +95,8 @@ {{- if not (eq .Values.topology "Client") }} {{- $ingress := .Values._namespace.ingress }} {{- $host := .Values._namespace.host }} +{{- $solver := (index .Values._cluster "solver") | default "http01" }} +{{- $clusterIssuer := (index .Values._cluster "issuer-name") | default "letsencrypt-prod" }} apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: @@ -92,30 +153,77 @@ spec: storageClass: {{ . }} {{- end }} maxVolumes: 0 - {{ if eq .Values.topology "MultiZone" }} + {{- if .Values.volume.diskType }} + extraArgs: + - "-disk={{ .Values.volume.diskType }}" + {{- end }} + {{- if or (and (eq .Values.topology "Simple") (gt (len .Values.volume.pools) 0)) (eq .Values.topology "MultiZone") }} volumes: - {{- range $zoneName, $zone := .Values.volume.zones }} - {{ $zoneName }}: - {{ with $zone.replicas }} - replicas: {{ . }} - {{- end }} + {{- if eq .Values.topology "Simple" }} + {{- range $poolName, $pool := .Values.volume.pools }} + {{ $poolName }}: + replicas: {{ ternary $pool.replicas $.Values.volume.replicas (hasKey $pool "replicas") }} + resources: {{- include "cozy-lib.resources.defaultingSanitize" (list ($pool.resourcesPreset | default $.Values.volume.resourcesPreset) (default dict $pool.resources) $) | nindent 12 }} dataDirs: - name: data1 type: "persistentVolumeClaim" - {{- if $zone.size }} - size: "{{ $zone.size }}" - {{- else }} - size: "{{ $.Values.volume.size }}" + size: "{{ $pool.size | default $.Values.volume.size }}" + {{- with ($pool.storageClass | default $.Values.volume.storageClass) }} + storageClass: "{{ . }}" {{- end }} - {{- if $zone.storageClass }} - storageClass: {{ $zone.storageClass }} - {{- else if $.Values.volume.storageClass }} - storageClass: {{ $.Values.volume.storageClass }} + maxVolumes: 0 + extraArgs: + - "-disk={{ $pool.diskType }}" + {{- end }} + {{- else if eq .Values.topology "MultiZone" }} + {{- range $zoneName, $zone := .Values.volume.zones }} + {{ $zoneName }}: + replicas: {{ ternary $zone.replicas $.Values.volume.replicas (hasKey $zone "replicas") }} + resources: {{- include "cozy-lib.resources.defaultingSanitize" (list $.Values.volume.resourcesPreset $.Values.volume.resources $) | nindent 12 }} + dataDirs: + - name: data1 + type: "persistentVolumeClaim" + size: "{{ $zone.size | default $.Values.volume.size }}" + {{- with ($zone.storageClass | default $.Values.volume.storageClass) }} + storageClass: "{{ . }}" {{- end }} maxVolumes: 0 nodeSelector: | + {{- with $zone.nodeSelector }} +{{ . | indent 12 }} + {{- else }} topology.kubernetes.io/zone: {{ $zoneName }} + {{- end }} dataCenter: {{ $zone.dataCenter | default $zoneName }} + {{- if $.Values.volume.diskType }} + extraArgs: + - "-disk={{ $.Values.volume.diskType }}" + {{- end }} + {{- end }} + {{- range $zoneName, $zone := .Values.volume.zones }} + {{- range $poolName, $pool := (dig "pools" dict $zone) }} + {{ $zoneName }}-{{ $poolName }}: + replicas: {{ ternary $pool.replicas (ternary $zone.replicas $.Values.volume.replicas (hasKey $zone "replicas")) (hasKey $pool "replicas") }} + resources: {{- include "cozy-lib.resources.defaultingSanitize" (list ($pool.resourcesPreset | default $.Values.volume.resourcesPreset) (default dict $pool.resources) $) | nindent 12 }} + dataDirs: + - name: data1 + type: "persistentVolumeClaim" + size: "{{ $pool.size | default $zone.size | default $.Values.volume.size }}" + {{- with ($pool.storageClass | default $zone.storageClass | default $.Values.volume.storageClass) }} + storageClass: "{{ . }}" + {{- end }} + maxVolumes: 0 + nodeSelector: | + {{- with $zone.nodeSelector }} +{{ . | indent 12 }} + {{- else }} + topology.kubernetes.io/zone: {{ $zoneName }} + {{- end }} + dataCenter: {{ $zone.dataCenter | default $zoneName }} + extraArgs: + - "-disk={{ $pool.diskType }}" + {{- end }} + {{- end }} {{- end }} {{- end }} filer: @@ -134,8 +242,10 @@ spec: annotations: nginx.ingress.kubernetes.io/proxy-body-size: "0" nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" - acme.cert-manager.io/http01-ingress-class: {{ $ingress }} - cert-manager.io/cluster-issuer: letsencrypt-prod + {{- if eq $solver "http01" }} + acme.cert-manager.io/http01-ingress-ingressclassname: {{ $ingress }} + {{- end }} + cert-manager.io/cluster-issuer: {{ $clusterIssuer }} tls: - hosts: - {{ .Values.host | default (printf "s3.%s" $host) }} @@ -195,6 +305,22 @@ spec: app.kubernetes.io/component: volume app.kubernetes.io/name: seaweedfs version: {{ $.Chart.Version }} +{{- range $poolName, $pool := .Values.volume.pools }} +--- +apiVersion: cozystack.io/v1alpha1 +kind: WorkloadMonitor +metadata: + name: {{ $.Release.Name }}-volume-{{ $poolName }} +spec: + replicas: {{ ternary $pool.replicas $.Values.volume.replicas (hasKey $pool "replicas") }} + minReplicas: 1 + kind: seaweedfs + type: volume + selector: + app.kubernetes.io/component: volume-{{ $poolName }} + app.kubernetes.io/name: seaweedfs + version: {{ $.Chart.Version }} +{{- end }} {{- else if eq .Values.topology "MultiZone" }} {{- range $zoneName, $zoneSpec := .Values.volume.zones }} --- @@ -203,7 +329,7 @@ kind: WorkloadMonitor metadata: name: {{ $.Release.Name }}-volume-{{ $zoneName }} spec: - replicas: {{ default $.Values.volume.replicas $zoneSpec.replicas }} + replicas: {{ ternary $zoneSpec.replicas $.Values.volume.replicas (hasKey $zoneSpec "replicas") }} minReplicas: 1 kind: seaweedfs type: volume @@ -211,6 +337,22 @@ spec: app.kubernetes.io/component: volume-{{ $zoneName }} app.kubernetes.io/name: seaweedfs version: {{ $.Chart.Version }} +{{- range $poolName, $pool := (dig "pools" dict $zoneSpec) }} +--- +apiVersion: cozystack.io/v1alpha1 +kind: WorkloadMonitor +metadata: + name: {{ $.Release.Name }}-volume-{{ $zoneName }}-{{ $poolName }} +spec: + replicas: {{ ternary $pool.replicas (ternary $zoneSpec.replicas $.Values.volume.replicas (hasKey $zoneSpec "replicas")) (hasKey $pool "replicas") }} + minReplicas: 1 + kind: seaweedfs + type: volume + selector: + app.kubernetes.io/component: volume-{{ $zoneName }}-{{ $poolName }} + app.kubernetes.io/name: seaweedfs + version: {{ $.Chart.Version }} +{{- end }} {{- end }} {{- end }} --- diff --git a/packages/extra/seaweedfs/templates/storage-pool-bucket-classes.yaml b/packages/extra/seaweedfs/templates/storage-pool-bucket-classes.yaml new file mode 100644 index 00000000..bf58340a --- /dev/null +++ b/packages/extra/seaweedfs/templates/storage-pool-bucket-classes.yaml @@ -0,0 +1,55 @@ +{{- if ne .Values.topology "Client" }} +{{- /* Collect unique pools from volume.pools and zones[].pools */ -}} +{{- $uniquePools := dict }} +{{- range $poolName, $pool := .Values.volume.pools }} +{{- $_ := set $uniquePools $poolName $pool.diskType }} +{{- end }} +{{- if eq .Values.topology "MultiZone" }} +{{- range $zoneName, $zone := .Values.volume.zones }} +{{- range $poolName, $pool := (dig "pools" dict $zone) }} +{{- $_ := set $uniquePools $poolName $pool.diskType }} +{{- end }} +{{- end }} +{{- end }} +{{- range $poolName, $diskType := $uniquePools }} +--- +kind: BucketClass +apiVersion: objectstorage.k8s.io/v1alpha1 +metadata: + name: {{ $.Release.Namespace }}-{{ $poolName }} +driverName: {{ $.Release.Namespace }}.seaweedfs.objectstorage.k8s.io +deletionPolicy: Delete +parameters: + disk: {{ $diskType }} +--- +kind: BucketClass +apiVersion: objectstorage.k8s.io/v1alpha1 +metadata: + name: {{ $.Release.Namespace }}-{{ $poolName }}-lock +driverName: {{ $.Release.Namespace }}.seaweedfs.objectstorage.k8s.io +deletionPolicy: Retain +parameters: + disk: {{ $diskType }} + objectLockEnabled: "true" + objectLockRetentionMode: "COMPLIANCE" + objectLockRetentionDays: "365" +--- +kind: BucketAccessClass +apiVersion: objectstorage.k8s.io/v1alpha1 +metadata: + name: {{ $.Release.Namespace }}-{{ $poolName }} +driverName: {{ $.Release.Namespace }}.seaweedfs.objectstorage.k8s.io +authenticationType: KEY +parameters: + accessPolicy: readwrite +--- +kind: BucketAccessClass +apiVersion: objectstorage.k8s.io/v1alpha1 +metadata: + name: {{ $.Release.Namespace }}-{{ $poolName }}-readonly +driverName: {{ $.Release.Namespace }}.seaweedfs.objectstorage.k8s.io +authenticationType: KEY +parameters: + accessPolicy: readonly +{{- end }} +{{- end }} diff --git a/packages/extra/seaweedfs/values.schema.json b/packages/extra/seaweedfs/values.schema.json index 4003df9e..9b48043d 100644 --- a/packages/extra/seaweedfs/values.schema.json +++ b/packages/extra/seaweedfs/values.schema.json @@ -2,6 +2,26 @@ "title": "Chart Values", "type": "object", "properties": { + "host": { + "description": "The hostname used to access SeaweedFS externally (defaults to 's3' subdomain for the tenant host).", + "type": "string", + "default": "" + }, + "topology": { + "description": "The topology of the SeaweedFS cluster.", + "type": "string", + "default": "Simple", + "enum": [ + "Simple", + "MultiZone", + "Client" + ] + }, + "replicationFactor": { + "description": "Replication factor: number of replicas for each volume in the SeaweedFS cluster.", + "type": "integer", + "default": 2 + }, "db": { "description": "Database configuration.", "type": "object", @@ -80,6 +100,65 @@ } } }, + "master": { + "description": "Master service configuration.", + "type": "object", + "default": {}, + "properties": { + "replicas": { + "description": "Number of master replicas.", + "type": "integer", + "default": 3 + }, + "resources": { + "description": "Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.", + "type": "object", + "default": {}, + "properties": { + "cpu": { + "description": "Number of CPU cores allocated.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "memory": { + "description": "Amount of memory allocated.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + } + } + }, + "resourcesPreset": { + "description": "Default sizing preset used when `resources` is omitted.", + "type": "string", + "default": "small", + "enum": [ + "nano", + "micro", + "small", + "medium", + "large", + "xlarge", + "2xlarge" + ] + } + } + }, "filer": { "description": "Filer service configuration.", "type": "object", @@ -157,149 +236,99 @@ } } }, - "host": { - "description": "The hostname used to access SeaweedFS externally (defaults to 's3' subdomain for the tenant host).", - "type": "string", - "default": "" - }, - "master": { - "description": "Master service configuration.", - "type": "object", - "default": {}, - "properties": { - "replicas": { - "description": "Number of master replicas.", - "type": "integer", - "default": 3 - }, - "resources": { - "description": "Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.", - "type": "object", - "default": {}, - "properties": { - "cpu": { - "description": "Number of CPU cores allocated.", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - }, - "memory": { - "description": "Amount of memory allocated.", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - } - } - }, - "resourcesPreset": { - "description": "Default sizing preset used when `resources` is omitted.", - "type": "string", - "default": "small", - "enum": [ - "nano", - "micro", - "small", - "medium", - "large", - "xlarge", - "2xlarge" - ] - } - } - }, - "replicationFactor": { - "description": "Replication factor: number of replicas for each volume in the SeaweedFS cluster.", - "type": "integer", - "default": 2 - }, - "s3": { - "description": "S3 service configuration.", - "type": "object", - "default": {}, - "properties": { - "replicas": { - "description": "Number of S3 replicas.", - "type": "integer", - "default": 2 - }, - "resources": { - "description": "Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.", - "type": "object", - "default": {}, - "properties": { - "cpu": { - "description": "Number of CPU cores allocated.", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - }, - "memory": { - "description": "Amount of memory allocated.", - "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "string" - } - ], - "x-kubernetes-int-or-string": true - } - } - }, - "resourcesPreset": { - "description": "Default sizing preset used when `resources` is omitted.", - "type": "string", - "default": "small", - "enum": [ - "nano", - "micro", - "small", - "medium", - "large", - "xlarge", - "2xlarge" - ] - } - } - }, - "topology": { - "description": "The topology of the SeaweedFS cluster.", - "type": "string", - "default": "Simple", - "enum": [ - "Simple", - "MultiZone", - "Client" - ] - }, "volume": { "description": "Volume service configuration.", "type": "object", "default": {}, "properties": { + "diskType": { + "description": "SeaweedFS disk type tag for the default volume servers (e.g., \"hdd\", \"ssd\").", + "type": "string", + "default": "" + }, + "pools": { + "description": "A map of storage pools. Each pool creates a separate Volume StatefulSet with its own disk type.", + "type": "object", + "default": {}, + "additionalProperties": { + "type": "object", + "required": [ + "diskType" + ], + "properties": { + "diskType": { + "description": "SeaweedFS disk type tag (e.g., \"ssd\", \"hdd\", \"nvme\").", + "type": "string" + }, + "replicas": { + "description": "Number of volume replicas. Defaults to volume.replicas (Simple) or zone.replicas/volume.replicas (MultiZone).", + "type": "integer" + }, + "resources": { + "description": "Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.", + "type": "object", + "properties": { + "cpu": { + "description": "Number of CPU cores allocated.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "memory": { + "description": "Amount of memory allocated.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + } + } + }, + "resourcesPreset": { + "description": "Default sizing preset used when `resources` is omitted. Defaults to volume.resourcesPreset.", + "type": "string", + "enum": [ + "nano", + "micro", + "small", + "medium", + "large", + "xlarge", + "2xlarge" + ] + }, + "size": { + "description": "Persistent Volume size. Defaults to volume.size (Simple) or zone.size/volume.size (MultiZone).", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "storageClass": { + "description": "Kubernetes StorageClass for the pool. Defaults to volume.storageClass.", + "type": "string" + } + } + } + }, "replicas": { "description": "Number of volume replicas.", "type": "integer", @@ -378,6 +407,96 @@ "additionalProperties": { "type": "object", "properties": { + "dataCenter": { + "description": "SeaweedFS data center name for this zone. Defaults to the zone name.", + "type": "string" + }, + "nodeSelector": { + "description": "YAML nodeSelector for this zone (default: topology.kubernetes.io/zone: \u003czoneName\u003e).", + "type": "string" + }, + "pools": { + "description": "A map of storage pools for this zone. Each pool creates a separate Volume StatefulSet per zone.", + "type": "object", + "additionalProperties": { + "type": "object", + "required": [ + "diskType" + ], + "properties": { + "diskType": { + "description": "SeaweedFS disk type tag (e.g., \"ssd\", \"hdd\", \"nvme\").", + "type": "string" + }, + "replicas": { + "description": "Number of volume replicas. Defaults to volume.replicas (Simple) or zone.replicas/volume.replicas (MultiZone).", + "type": "integer" + }, + "resources": { + "description": "Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.", + "type": "object", + "properties": { + "cpu": { + "description": "Number of CPU cores allocated.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "memory": { + "description": "Amount of memory allocated.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + } + } + }, + "resourcesPreset": { + "description": "Default sizing preset used when `resources` is omitted. Defaults to volume.resourcesPreset.", + "type": "string", + "enum": [ + "nano", + "micro", + "small", + "medium", + "large", + "xlarge", + "2xlarge" + ] + }, + "size": { + "description": "Persistent Volume size. Defaults to volume.size (Simple) or zone.size/volume.size (MultiZone).", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "storageClass": { + "description": "Kubernetes StorageClass for the pool. Defaults to volume.storageClass.", + "type": "string" + } + } + } + }, "replicas": { "description": "Number of replicas in the zone.", "type": "integer" @@ -394,11 +513,74 @@ } ], "x-kubernetes-int-or-string": true + }, + "storageClass": { + "description": "StorageClass used to store zone data. Defaults to volume.storageClass.", + "type": "string" } } } } } + }, + "s3": { + "description": "S3 service configuration.", + "type": "object", + "default": {}, + "properties": { + "replicas": { + "description": "Number of S3 replicas.", + "type": "integer", + "default": 2 + }, + "resources": { + "description": "Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.", + "type": "object", + "default": {}, + "properties": { + "cpu": { + "description": "Number of CPU cores allocated.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + }, + "memory": { + "description": "Amount of memory allocated.", + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "x-kubernetes-int-or-string": true + } + } + }, + "resourcesPreset": { + "description": "Default sizing preset used when `resources` is omitted.", + "type": "string", + "default": "small", + "enum": [ + "nano", + "micro", + "small", + "medium", + "large", + "xlarge", + "2xlarge" + ] + } + } } } -} \ No newline at end of file +} diff --git a/packages/extra/seaweedfs/values.yaml b/packages/extra/seaweedfs/values.yaml index e2fc02fd..9965d5d0 100644 --- a/packages/extra/seaweedfs/values.yaml +++ b/packages/extra/seaweedfs/values.yaml @@ -76,26 +76,49 @@ filer: grpcPort: 443 whitelist: [] +## @typedef {struct} StoragePool - Storage pool configuration for separating buckets by disk type. +## @field {string} diskType - SeaweedFS disk type tag (e.g., "ssd", "hdd", "nvme"). +## @field {int} [replicas] - Number of volume replicas. Defaults to volume.replicas (Simple) or zone.replicas/volume.replicas (MultiZone). +## @field {quantity} [size] - Persistent Volume size. Defaults to volume.size (Simple) or zone.size/volume.size (MultiZone). +## @field {string} [storageClass] - Kubernetes StorageClass for the pool. Defaults to volume.storageClass. +## @field {Resources} [resources] - Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. +## @field {ResourcesPreset} [resourcesPreset] - Default sizing preset used when `resources` is omitted. Defaults to volume.resourcesPreset. + ## @typedef {struct} Zone - Zone configuration. ## @field {int} [replicas] - Number of replicas in the zone. ## @field {quantity} [size] - Zone storage size. +## @field {string} [dataCenter] - SeaweedFS data center name for this zone. Defaults to the zone name. +## @field {string} [nodeSelector] - YAML nodeSelector for this zone (default: topology.kubernetes.io/zone: ). +## @field {string} [storageClass] - StorageClass used to store zone data. Defaults to volume.storageClass. +## @field {map[string]StoragePool} [pools] - A map of storage pools for this zone. Each pool creates a separate Volume StatefulSet per zone. +## NOTE: Zone-level resources/resourcesPreset are inherited from volume.* settings. Pools within a zone can define their own resources. ## @typedef {struct} Volume - Volume service configuration. ## @field {int} [replicas] - Number of volume replicas. ## @field {quantity} [size] - Persistent Volume size. ## @field {string} [storageClass] - StorageClass used to store the data. +## @field {string} [diskType] - SeaweedFS disk type tag for the default volume servers (e.g., "hdd", "ssd"). ## @field {Resources} [resources] - Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. ## @field {ResourcesPreset} [resourcesPreset] - Default sizing preset used when `resources` is omitted. ## @field {map[string]Zone} [zones] - A map of zones for MultiZone topology. Each zone can have its own number of replicas and size. +## @field {map[string]StoragePool} [pools] - A map of storage pools. Each pool creates a separate Volume StatefulSet with its own disk type. ## @param {Volume} [volume] - Volume service configuration. volume: replicas: 2 size: 10Gi storageClass: "" + diskType: "" resources: {} resourcesPreset: "small" zones: {} + pools: {} + #pools: + # fast: + # diskType: ssd + # replicas: 2 + # size: 50Gi + # storageClass: "local-nvme" ## @typedef {struct} S3 - S3 service configuration. ## @field {int} [replicas] - Number of S3 replicas. diff --git a/packages/library/cozy-lib/templates/_strings.tpl b/packages/library/cozy-lib/templates/_strings.tpl new file mode 100644 index 00000000..0ba8153f --- /dev/null +++ b/packages/library/cozy-lib/templates/_strings.tpl @@ -0,0 +1,3 @@ +{{- define "cozy-lib.strings.hexToInt" }} +{{- printf "num: 0x%s" . | fromYaml | dig "num" 0 }} +{{- end }} diff --git a/packages/system/backup-controller/definitions/backups.cozystack.io_backups.yaml b/packages/system/backup-controller/definitions/backups.cozystack.io_backups.yaml index 6d55cb84..d48df533 100644 --- a/packages/system/backup-controller/definitions/backups.cozystack.io_backups.yaml +++ b/packages/system/backup-controller/definitions/backups.cozystack.io_backups.yaml @@ -205,6 +205,14 @@ spec: Phase is a simple, high-level summary of the backup's state. Typical values are: Pending, Ready, Failed. type: string + underlyingResources: + description: |- + UnderlyingResources holds application-specific resource metadata discovered + during backup (e.g., VM disks, network configuration). The payload is a + self-typed JSON object carrying an inlined TypeMeta (kind/apiVersion) so + the consuming controller can dispatch on the application kind. + type: object + x-kubernetes-preserve-unknown-fields: true type: object type: object selectableFields: diff --git a/packages/system/backup-controller/definitions/backups.cozystack.io_restorejobs.yaml b/packages/system/backup-controller/definitions/backups.cozystack.io_restorejobs.yaml index 400737fc..b9465994 100644 --- a/packages/system/backup-controller/definitions/backups.cozystack.io_restorejobs.yaml +++ b/packages/system/backup-controller/definitions/backups.cozystack.io_restorejobs.yaml @@ -59,6 +59,12 @@ spec: type: string type: object x-kubernetes-map-type: atomic + options: + description: |- + Options is a driver-specific blob of restore options, typed based on + targetApplicationRef and the current controller implementation. + type: object + x-kubernetes-preserve-unknown-fields: true targetApplicationRef: description: |- TargetApplicationRef refers to the application into which the backup diff --git a/packages/system/backup-controller/templates/rbac.yaml b/packages/system/backup-controller/templates/rbac.yaml index 255398e8..da2460c0 100644 --- a/packages/system/backup-controller/templates/rbac.yaml +++ b/packages/system/backup-controller/templates/rbac.yaml @@ -3,30 +3,21 @@ apiVersion: rbac.authorization.k8s.io/v1 metadata: name: backups.cozystack.io:core-controller rules: +# Plan: reconcile schedule and update status - apiGroups: ["backups.cozystack.io"] resources: ["plans"] verbs: ["get", "list", "watch"] - apiGroups: ["backups.cozystack.io"] - resources: ["backupjobs"] - verbs: ["create", "get", "list", "watch", "update", "patch"] -- apiGroups: ["backups.cozystack.io"] - resources: ["backupjobs/status"] + resources: ["plans/status"] verbs: ["get", "update", "patch"] +# BackupJob: create when schedule fires (status is updated by backupstrategy-controller) - apiGroups: ["backups.cozystack.io"] - resources: ["backups"] + resources: ["backupjobs"] verbs: ["create", "get", "list", "watch"] -- apiGroups: ["apps.cozystack.io"] - resources: ["buckets", "bucketaccesses", "virtualmachines"] - verbs: ["get", "list", "watch"] -- apiGroups: ["objectstorage.k8s.io"] - resources: ["buckets", "bucketaccesses"] - verbs: ["get", "list", "watch"] +# Leader election (--leader-elect) +- apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["get", "list", "watch", "create", "update", "patch"] - apiGroups: [""] - resources: ["secrets"] - verbs: ["create", "get", "list", "watch", "update", "patch"] -- apiGroups: ["kubevirt.io"] - resources: ["virtualmachines"] - verbs: ["get", "list", "watch"] -- apiGroups: ["velero.io"] - resources: ["backups", "backupstoragelocations", "volumesnapshotlocations", "restores"] - verbs: ["create", "get", "list", "watch", "update", "patch"] + resources: ["events"] + verbs: ["create", "patch"] diff --git a/packages/system/backup-controller/values.yaml b/packages/system/backup-controller/values.yaml index 9ce9bac9..34e033ce 100644 --- a/packages/system/backup-controller/values.yaml +++ b/packages/system/backup-controller/values.yaml @@ -1,5 +1,5 @@ backupController: - image: "ghcr.io/cozystack/cozystack/backup-controller:v1.0.0-beta.6@sha256:365214a74ffc34a9314a62a7d4b491590051fc5486f6bae9913c0c1289983d43" + image: "ghcr.io/cozystack/cozystack/backup-controller:v1.3.0@sha256:e1a083dc92f26dfef004f47c1cd20a6357174aad835004f58e751c494b76649a" replicas: 2 debug: false metrics: diff --git a/packages/system/backupstrategy-controller/templates/rbac.yaml b/packages/system/backupstrategy-controller/templates/rbac.yaml index 85431a4b..634ea88b 100644 --- a/packages/system/backupstrategy-controller/templates/rbac.yaml +++ b/packages/system/backupstrategy-controller/templates/rbac.yaml @@ -3,9 +3,80 @@ apiVersion: rbac.authorization.k8s.io/v1 metadata: name: backups.cozystack.io:strategy-controller rules: +# Strategy types (Velero, Job) - apiGroups: ["strategy.backups.cozystack.io"] resources: ["*"] verbs: ["get", "list", "watch"] +# BackupClass: resolve strategy per application +- apiGroups: ["backups.cozystack.io"] + resources: ["backupclasses"] + verbs: ["get", "list", "watch"] +# BackupJob / RestoreJob: reconcile; update for RestoreJob finalizers - apiGroups: ["backups.cozystack.io"] resources: ["backupjobs", "restorejobs"] + verbs: ["get", "list", "watch", "update", "patch"] +- apiGroups: ["backups.cozystack.io"] + resources: ["backupjobs/status", "restorejobs/status"] + verbs: ["get", "update", "patch"] +# Backup: create after Velero job completes; update/patch for BackupReconciler finalizers +- apiGroups: ["backups.cozystack.io"] + resources: ["backups"] + verbs: ["create", "get", "list", "watch", "update", "patch"] +# Pods: BackupJob lists virt-launcher pods by label (manager cache uses cluster-scoped list/watch) +- apiGroups: [""] + resources: ["pods"] verbs: ["get", "list", "watch"] +# ConfigMaps: controller-runtime cache requires cluster-scoped list/watch; +# create/update/delete is scoped to cozy-velero via a Role shipped by the +# velero chart (packages/system/velero/templates/backupstrategy-controller-rbac.yaml) +# so it is only created when velero is installed. +- apiGroups: [""] + resources: ["configmaps"] + verbs: ["get", "list", "watch"] +# Application refs (e.g. VMInstance) for backup/restore scope +- apiGroups: ["apps.cozystack.io"] + resources: ["*"] + verbs: ["get", "list", "watch"] +# KubeVirt: pre-restore halts VMs and checks VMI shutdown +- apiGroups: ["kubevirt.io"] + resources: ["virtualmachines"] + verbs: ["get", "update"] +- apiGroups: ["kubevirt.io"] + resources: ["virtualmachineinstances"] + verbs: ["get"] +# CDI DataVolumes: pre-restore deletes DVs so CDI doesn't recreate PVCs after rename +- apiGroups: ["cdi.kubevirt.io"] + resources: ["datavolumes"] + verbs: ["delete"] +# HelmReleases: pre-restore suspends HRs; post-restore rename creates new HR and deletes old +- apiGroups: ["helm.toolkit.fluxcd.io"] + resources: ["helmreleases"] + verbs: ["get", "create", "update", "delete"] +# PVCs and PVs: pre-restore renames PVCs (delete old, create new) and patches PV reclaim policy +- apiGroups: [""] + resources: ["persistentvolumeclaims"] + verbs: ["get", "list", "watch", "create", "delete"] +- apiGroups: [""] + resources: ["persistentvolumes"] + verbs: ["get", "list", "watch", "update"] +# Velero Backup/Restore/DataUpload in cozy-velero namespace (DataUpload: enrich BackupJob failure messages) +- apiGroups: ["velero.io"] + resources: ["backups", "restores", "datauploads"] + verbs: ["create", "get", "list", "watch", "update", "patch", "delete", "deletecollection"] +# Velero DeleteBackupRequest: used by BackupReconciler to delete Velero backups with their storage data +- apiGroups: ["velero.io"] + resources: ["deletebackuprequests"] + verbs: ["create", "get", "list", "watch"] +# Namespaces: validate target namespace exists for cross-namespace restore +# controller-runtime cache requires list/watch for any resource accessed via r.Get() +- apiGroups: [""] + resources: ["namespaces"] + verbs: ["get", "list", "watch"] +# Events from Recorder.Event() calls +- apiGroups: [""] + resources: ["events"] + verbs: ["create", "patch"] +# Leader election (--leader-elect) +- apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["get", "list", "watch", "create", "update", "patch"] diff --git a/packages/system/backupstrategy-controller/values.yaml b/packages/system/backupstrategy-controller/values.yaml index d74557a0..d6453124 100644 --- a/packages/system/backupstrategy-controller/values.yaml +++ b/packages/system/backupstrategy-controller/values.yaml @@ -1,5 +1,5 @@ backupStrategyController: - image: "ghcr.io/cozystack/cozystack/backupstrategy-controller:v1.0.0-beta.6@sha256:aa04ee61dce11950162606fc8db2d5cbc6f5b32ba700f790b3f1eee10d65efb1" + image: "ghcr.io/cozystack/cozystack/backupstrategy-controller:v1.3.0@sha256:be0a9ec1f4307064b16388a24628aee46e06252738338add80b99ea1e04e62bf" replicas: 2 debug: false metrics: diff --git a/packages/system/bootbox-rd/cozyrds/bootbox.yaml b/packages/system/bootbox-rd/cozyrds/bootbox.yaml index 62e87931..667740e3 100644 --- a/packages/system/bootbox-rd/cozyrds/bootbox.yaml +++ b/packages/system/bootbox-rd/cozyrds/bootbox.yaml @@ -8,7 +8,7 @@ spec: plural: bootboxes singular: bootbox openAPISchema: |- - {"title":"Chart Values","type":"object","properties":{"machines":{"description":"Configuration of physical machine instances.","type":"array","default":[],"items":{"type":"object","required":["arch","hostname","ip","leaseTime","uefi"],"properties":{"arch":{"description":"Architecture.","type":"string"},"hostname":{"description":"Hostname.","type":"string"},"ip":{"description":"IP address configuration.","type":"object","required":["address","gateway","netmask"],"properties":{"address":{"description":"IP address.","type":"string"},"gateway":{"description":"IP gateway.","type":"string"},"netmask":{"description":"Netmask.","type":"string"}}},"leaseTime":{"description":"Lease time.","type":"integer"},"mac":{"description":"MAC addresses.","type":"array","items":{"type":"string"}},"nameServers":{"description":"Name servers.","type":"array","items":{"type":"string"}},"timeServers":{"description":"Time servers.","type":"array","items":{"type":"string"}},"uefi":{"description":"UEFI.","type":"boolean"}}}},"whitelist":{"description":"List of client networks.","type":"array","default":[],"items":{"type":"string"}},"whitelistHTTP":{"description":"Secure HTTP by enabling client networks whitelisting.","type":"boolean","default":true}}} + {"title":"Chart Values","type":"object","properties":{"whitelistHTTP":{"description":"Secure HTTP by enabling client networks whitelisting.","type":"boolean","default":true},"whitelist":{"description":"List of client networks.","type":"array","default":[],"items":{"type":"string"}},"machines":{"description":"Configuration of physical machine instances.","type":"array","default":[],"items":{"type":"object","required":["arch","hostname","ip","leaseTime","uefi"],"properties":{"arch":{"description":"Architecture.","type":"string"},"hostname":{"description":"Hostname.","type":"string"},"ip":{"description":"IP address configuration.","type":"object","required":["address","gateway","netmask"],"properties":{"address":{"description":"IP address.","type":"string"},"gateway":{"description":"IP gateway.","type":"string"},"netmask":{"description":"Netmask.","type":"string"}}},"leaseTime":{"description":"Lease time.","type":"integer"},"mac":{"description":"MAC addresses.","type":"array","items":{"type":"string"}},"nameServers":{"description":"Name servers.","type":"array","items":{"type":"string"}},"timeServers":{"description":"Time servers.","type":"array","items":{"type":"string"}},"uefi":{"description":"UEFI.","type":"boolean"}}}}}} release: prefix: "" labels: diff --git a/packages/system/bucket-rd/cozyrds/bucket.yaml b/packages/system/bucket-rd/cozyrds/bucket.yaml index 2a19f89e..7eab56b6 100644 --- a/packages/system/bucket-rd/cozyrds/bucket.yaml +++ b/packages/system/bucket-rd/cozyrds/bucket.yaml @@ -8,7 +8,7 @@ spec: plural: buckets singular: bucket openAPISchema: |- - {"title":"Chart Values","type":"object","properties":{}} + {"title":"Chart Values","type":"object","properties":{"locking":{"description":"Provisions bucket from the `-lock` BucketClass (with object lock enabled).","type":"boolean","default":false},"storagePool":{"description":"Selects a specific BucketClass by storage pool name.","type":"string","default":""},"users":{"description":"Users configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"readonly":{"description":"Whether the user has read-only access.","type":"boolean"}}}}}} release: prefix: bucket- labels: @@ -26,13 +26,14 @@ spec: tags: - storage icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82ODNfMzA5MSkiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03MiAzMC4xNjQxTDExNy45ODMgMzYuNzc4OVY0MC42NzM5QzExNy45ODMgNDYuNDY1MyA5Ny4zODYyIDUxLjEzMzIgNzEuOTgyNyA1MS4xMzMyQzQ2LjU3OTIgNTEuMTMzMiAyNiA0Ni40NjUzIDI2IDQwLjY3MzlWMzYuNDQzMUw3MiAzMC4xNjQxWk03MiA1OC4yNjc4QzkxLjIwODQgNTguMjY3OCAxMDcuNjU4IDU1LjU5ODYgMTE0LjU0NyA1MS44MDQ4TDExNi44MDMgNDguMTExTDExNy43MjMgNDQuNzUzVjQ4LjkxNzFMMTAyLjY3OSAxMTEuMDMzQzEwMi42NzkgMTE0Ljg5NSA4OC45NTMzIDExOCA3Mi4wMTcyIDExOEM1NS4wODEyIDExOCA0MS4zNzQzIDExNC44OTUgNDEuMzc0MyAxMTEuMDMzTDI2LjMzIDQ4LjkxNzFWNDQuODM2OUwyOS44MDA3IDUxLjkzODJDMzYuNzA2NSA1NS42NjUzIDUyLjk5OTcgNTguMjY3OCA3MiA1OC4yNjc4WiIgZmlsbD0iIzhDMzEyMyIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTcyLjAwMDMgMjZDOTcuNDAzOCAyNiAxMTggMzAuNjgzOSAxMTggMzYuNDQyQzExOCA0Mi4yIDk3LjM4NjYgNDYuODUwNyA3Mi4wMDAzIDQ2Ljg1MDdDNDYuNjE0MSA0Ni44NTA3IDI2LjAxNzYgNDIuMjM0NSAyNi4wMTc2IDM2LjQ0MkMyNi4wMTc2IDMwLjY0OTQgNDYuNTk2OCAyNiA3Mi4wMDAzIDI2Wk03Mi4wMDAzIDU0LjEwMzdDOTUuNjg1NyA1NC4xMDM3IDExNS4xNzIgNTAuMDU4IDExNy43MDYgNDQuODE5N0wxMDIuNjYyIDEwNi45MzdDMTAyLjY2MiAxMTAuNzk5IDg4LjkzNjQgMTEzLjkwNSA3Mi4wMDAzIDExMy45MDVDNTUuMDY0MyAxMTMuOTA1IDQxLjMzOSAxMTAuODE2IDQxLjMzOSAxMDYuOTU0TDI2LjI5NTkgNDQuODM3QzI4Ljg0NjYgNTAuMDU4IDQ4LjMzMzMgNTQuMTAzNyA3Mi4wMDAzIDU0LjEwMzdaIiBmaWxsPSIjRTA1MjQzIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNjEuMTcyNSA2MC4wMjkzSDgxLjA5MjhWNzkuMTY3Nkg2MS4xNzI1VjYwLjAyOTNaTTQ1LjMzMDEgOTUuMzY4OEM0NS4zMzAxIDkwLjE0MiA0OS43MTA0IDg1LjkzNDIgNTUuMTUxMSA4NS45MzQyQzYwLjU5MTcgODUuOTM0MiA2NC45NzIxIDkwLjE0MiA2NC45NzIxIDk1LjM2ODhDNjQuOTcyMSAxMDAuNTk2IDYwLjU5MTcgMTA0LjgwMyA1NS4xNTExIDEwNC44MDNDNDkuNzEwNCAxMDQuODAzIDQ1LjMzMDEgMTAwLjU5NiA0NS4zMzAxIDk1LjM2ODhaTTk2LjQ0ODcgMTA0LjM2OEg3Ni43NzIyTDg2LjYxMDUgODYuNzczN0w5Ni40NDg3IDEwNC4zNjhaIiBmaWxsPSJ3aGl0ZSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzY4M18zMDkxIiB4MT0iMCIgeTE9IjAiIHgyPSIxNTEiIHkyPSIxODAiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0ZGRjBFRSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNFQzg4N0QiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4K - keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"]] + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "locking"], ["spec", "storagePool"], ["spec", "users"]] secrets: exclude: [] include: - resourceNames: - - bucket-{{ .name }} - bucket-{{ .name }}-credentials + - matchLabels: + apps.cozystack.io/user-secret: "true" ingresses: exclude: [] include: diff --git a/packages/system/bucket/images/s3manager.tag b/packages/system/bucket/images/s3manager.tag index 261f2041..889db0af 100644 --- a/packages/system/bucket/images/s3manager.tag +++ b/packages/system/bucket/images/s3manager.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:291427de7db54a1d19dc9c2c807bdcc664a14caa9538786f31317e8c01a4a008 +ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:fb65e734bbdbdaef2238769cc2ecfb54dbddaf1e0952ad438a9b3e26b9dbb4b5 diff --git a/packages/system/bucket/images/s3manager/Dockerfile b/packages/system/bucket/images/s3manager/Dockerfile index 179acead..e97adf07 100644 --- a/packages/system/bucket/images/s3manager/Dockerfile +++ b/packages/system/bucket/images/s3manager/Dockerfile @@ -9,6 +9,7 @@ WORKDIR /usr/src/app RUN wget -O- https://github.com/cloudlena/s3manager/archive/9a7c8e446b422f8973b8c461990f39fdafee9c27.tar.gz | tar -xzf- --strip 1 ADD cozystack.patch / RUN git apply /cozystack.patch +RUN go mod tidy RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -ldflags="-s -w" -a -installsuffix cgo -o bin/s3manager FROM docker.io/library/alpine:latest diff --git a/packages/system/bucket/images/s3manager/cozystack.patch b/packages/system/bucket/images/s3manager/cozystack.patch index f631b144..2f569042 100644 --- a/packages/system/bucket/images/s3manager/cozystack.patch +++ b/packages/system/bucket/images/s3manager/cozystack.patch @@ -1,3 +1,235 @@ +diff --git a/go.mod b/go.mod +index b5d8540..6ede8e8 100644 +--- a/go.mod ++++ b/go.mod +@@ -1,10 +1,11 @@ + module github.com/cloudlena/s3manager + +-go 1.22.5 ++go 1.23 + + require ( + github.com/cloudlena/adapters v0.0.0-20240708203353-a39be02cc801 + github.com/gorilla/mux v1.8.1 ++ github.com/gorilla/sessions v1.4.0 + github.com/matryer/is v1.4.1 + github.com/minio/minio-go/v7 v7.0.74 + github.com/spf13/viper v1.19.0 +@@ -16,6 +17,7 @@ require ( + github.com/go-ini/ini v1.67.0 // indirect + github.com/goccy/go-json v0.10.3 // indirect + github.com/google/uuid v1.6.0 // indirect ++ github.com/gorilla/securecookie v1.1.2 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/cpuid/v2 v2.2.8 // indirect +diff --git a/go.sum b/go.sum +index 1ea1b16..d7866ce 100644 +--- a/go.sum ++++ b/go.sum +@@ -16,10 +16,16 @@ github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= + github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= + github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= + github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= ++github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= ++github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= + github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= + github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= + github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= + github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= ++github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= ++github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= ++github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ= ++github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik= + github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= + github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= + github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +diff --git a/main.go b/main.go +index 2ffe8ab..723a1b8 100644 +--- a/main.go ++++ b/main.go +@@ -41,10 +41,12 @@ type configuration struct { + Timeout int32 + SseType string + SseKey string ++ LoginMode bool + } + + func parseConfiguration() configuration { + var accessKeyID, secretAccessKey, iamEndpoint string ++ var loginMode bool + + viper.AutomaticEnv() + +@@ -57,13 +59,10 @@ func parseConfiguration() configuration { + iamEndpoint = viper.GetString("IAM_ENDPOINT") + } else { + accessKeyID = viper.GetString("ACCESS_KEY_ID") +- if len(accessKeyID) == 0 { +- log.Fatal("please provide ACCESS_KEY_ID") +- } +- + secretAccessKey = viper.GetString("SECRET_ACCESS_KEY") +- if len(secretAccessKey) == 0 { +- log.Fatal("please provide SECRET_ACCESS_KEY") ++ if len(accessKeyID) == 0 || len(secretAccessKey) == 0 { ++ log.Println("ACCESS_KEY_ID or SECRET_ACCESS_KEY not set, starting in login mode") ++ loginMode = true + } + } + +@@ -115,6 +114,7 @@ func parseConfiguration() configuration { + Timeout: timeout, + SseType: sseType, + SseKey: sseKey, ++ LoginMode: loginMode, + } + } + +@@ -135,57 +135,96 @@ func main() { + log.Fatal(err) + } + +- // Set up S3 client +- opts := &minio.Options{ +- Secure: configuration.UseSSL, +- } +- if configuration.UseIam { +- opts.Creds = credentials.NewIAM(configuration.IamEndpoint) +- } else { +- var signatureType credentials.SignatureType +- +- switch configuration.SignatureType { +- case "V2": +- signatureType = credentials.SignatureV2 +- case "V4": +- signatureType = credentials.SignatureV4 +- case "V4Streaming": +- signatureType = credentials.SignatureV4Streaming +- case "Anonymous": +- signatureType = credentials.SignatureAnonymous +- default: +- log.Fatalf("Invalid SIGNATURE_TYPE: %s", configuration.SignatureType) ++ // Set up router ++ r := mux.NewRouter() ++ r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.FS(statics)))).Methods(http.MethodGet) ++ ++ if configuration.LoginMode { ++ // Login mode: no pre-configured S3 client, per-session credentials ++ sessionCfg := &s3manager.SessionConfig{ ++ Store: s3manager.NewSessionStore(), ++ Endpoint: configuration.Endpoint, ++ UseSSL: configuration.UseSSL, ++ SkipSSLVerify: configuration.SkipSSLVerification, ++ AllowDelete: configuration.AllowDelete, ++ ForceDownload: configuration.ForceDownload, ++ ListRecursive: configuration.ListRecursive, ++ SseInfo: sseType, ++ Templates: templates, + } + +- opts.Creds = credentials.NewStatic(configuration.AccessKeyID, configuration.SecretAccessKey, "", signatureType) +- } ++ // Public routes (no auth required) ++ r.Handle("/login", s3manager.HandleLoginView(templates)).Methods(http.MethodGet) ++ r.Handle("/login", s3manager.HandleLogin(sessionCfg)).Methods(http.MethodPost) ++ r.Handle("/logout", s3manager.HandleLogout(sessionCfg)).Methods(http.MethodPost) ++ ++ // Protected routes (auth required via middleware) ++ protected := mux.NewRouter() ++ protected.Handle("/", http.RedirectHandler("/buckets", http.StatusPermanentRedirect)).Methods(http.MethodGet) ++ protected.Handle("/buckets", s3manager.HandleBucketsViewDynamic(templates, configuration.AllowDelete)).Methods(http.MethodGet) ++ protected.PathPrefix("/buckets/").Handler(s3manager.HandleBucketViewDynamic(templates, configuration.AllowDelete, configuration.ListRecursive)).Methods(http.MethodGet) ++ protected.Handle("/api/buckets", s3manager.HandleCreateBucketDynamic()).Methods(http.MethodPost) ++ if configuration.AllowDelete { ++ protected.Handle("/api/buckets/{bucketName}", s3manager.HandleDeleteBucketDynamic()).Methods(http.MethodDelete) ++ } ++ protected.Handle("/api/buckets/{bucketName}/objects", s3manager.HandleCreateObjectDynamic(sseType)).Methods(http.MethodPost) ++ protected.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}/url", s3manager.HandleGenerateUrlDynamic()).Methods(http.MethodGet) ++ protected.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleGetObjectDynamic(configuration.ForceDownload)).Methods(http.MethodGet) ++ if configuration.AllowDelete { ++ protected.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleDeleteObjectDynamic()).Methods(http.MethodDelete) ++ } + +- if configuration.Region != "" { +- opts.Region = configuration.Region +- } +- if configuration.UseSSL && configuration.SkipSSLVerification { +- opts.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} //nolint:gosec +- } +- s3, err := minio.New(configuration.Endpoint, opts) +- if err != nil { +- log.Fatalln(fmt.Errorf("error creating s3 client: %w", err)) +- } ++ r.PathPrefix("/").Handler(s3manager.RequireAuth(sessionCfg, protected)) ++ } else { ++ // Pre-configured mode: existing behavior with static S3 client ++ opts := &minio.Options{ ++ Secure: configuration.UseSSL, ++ } ++ if configuration.UseIam { ++ opts.Creds = credentials.NewIAM(configuration.IamEndpoint) ++ } else { ++ var signatureType credentials.SignatureType ++ ++ switch configuration.SignatureType { ++ case "V2": ++ signatureType = credentials.SignatureV2 ++ case "V4": ++ signatureType = credentials.SignatureV4 ++ case "V4Streaming": ++ signatureType = credentials.SignatureV4Streaming ++ case "Anonymous": ++ signatureType = credentials.SignatureAnonymous ++ default: ++ log.Fatalf("Invalid SIGNATURE_TYPE: %s", configuration.SignatureType) ++ } ++ ++ opts.Creds = credentials.NewStatic(configuration.AccessKeyID, configuration.SecretAccessKey, "", signatureType) ++ } + +- // Set up router +- r := mux.NewRouter() +- r.Handle("/", http.RedirectHandler("/buckets", http.StatusPermanentRedirect)).Methods(http.MethodGet) +- r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.FS(statics)))).Methods(http.MethodGet) +- r.Handle("/buckets", s3manager.HandleBucketsView(s3, templates, configuration.AllowDelete)).Methods(http.MethodGet) +- r.PathPrefix("/buckets/").Handler(s3manager.HandleBucketView(s3, templates, configuration.AllowDelete, configuration.ListRecursive)).Methods(http.MethodGet) +- r.Handle("/api/buckets", s3manager.HandleCreateBucket(s3)).Methods(http.MethodPost) +- if configuration.AllowDelete { +- r.Handle("/api/buckets/{bucketName}", s3manager.HandleDeleteBucket(s3)).Methods(http.MethodDelete) +- } +- r.Handle("/api/buckets/{bucketName}/objects", s3manager.HandleCreateObject(s3, sseType)).Methods(http.MethodPost) +- r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}/url", s3manager.HandleGenerateUrl(s3)).Methods(http.MethodGet) +- r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleGetObject(s3, configuration.ForceDownload)).Methods(http.MethodGet) +- if configuration.AllowDelete { +- r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleDeleteObject(s3)).Methods(http.MethodDelete) ++ if configuration.Region != "" { ++ opts.Region = configuration.Region ++ } ++ if configuration.UseSSL && configuration.SkipSSLVerification { ++ opts.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} //nolint:gosec ++ } ++ s3, err := minio.New(configuration.Endpoint, opts) ++ if err != nil { ++ log.Fatalln(fmt.Errorf("error creating s3 client: %w", err)) ++ } ++ ++ r.Handle("/", http.RedirectHandler("/buckets", http.StatusPermanentRedirect)).Methods(http.MethodGet) ++ r.Handle("/buckets", s3manager.HandleBucketsView(s3, templates, configuration.AllowDelete)).Methods(http.MethodGet) ++ r.PathPrefix("/buckets/").Handler(s3manager.HandleBucketView(s3, templates, configuration.AllowDelete, configuration.ListRecursive)).Methods(http.MethodGet) ++ r.Handle("/api/buckets", s3manager.HandleCreateBucket(s3)).Methods(http.MethodPost) ++ if configuration.AllowDelete { ++ r.Handle("/api/buckets/{bucketName}", s3manager.HandleDeleteBucket(s3)).Methods(http.MethodDelete) ++ } ++ r.Handle("/api/buckets/{bucketName}/objects", s3manager.HandleCreateObject(s3, sseType)).Methods(http.MethodPost) ++ r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}/url", s3manager.HandleGenerateUrl(s3)).Methods(http.MethodGet) ++ r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleGetObject(s3, configuration.ForceDownload)).Methods(http.MethodGet) ++ if configuration.AllowDelete { ++ r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleDeleteObject(s3)).Methods(http.MethodDelete) ++ } + } + + lr := logging.Handler(os.Stdout)(r) diff --git a/web/template/bucket.html.tmpl b/web/template/bucket.html.tmpl index e2f8d28..87add13 100644 --- a/web/template/bucket.html.tmpl @@ -24,3 +256,298 @@ index c7ea184..fb1dce7 100644 +diff --git a/internal/app/s3manager/auth.go b/internal/app/s3manager/auth.go +new file mode 100644 +index 0000000..58589e2 +--- /dev/null ++++ b/internal/app/s3manager/auth.go +@@ -0,0 +1,237 @@ ++package s3manager ++ ++import ( ++ "context" ++ "crypto/rand" ++ "crypto/tls" ++ "fmt" ++ "html/template" ++ "io/fs" ++ "log" ++ "net/http" ++ ++ "github.com/gorilla/sessions" ++ "github.com/minio/minio-go/v7" ++ "github.com/minio/minio-go/v7/pkg/credentials" ++) ++ ++type contextKey string ++ ++const s3ContextKey contextKey = "s3client" ++ ++// SessionConfig holds session store and S3 connection settings for login mode. ++type SessionConfig struct { ++ Store *sessions.CookieStore ++ Endpoint string ++ UseSSL bool ++ SkipSSLVerify bool ++ AllowDelete bool ++ ForceDownload bool ++ ListRecursive bool ++ SseInfo SSEType ++ Templates fs.FS ++} ++ ++// NewSessionStore creates a CookieStore with a random encryption key. ++func NewSessionStore() *sessions.CookieStore { ++ key := make([]byte, 32) ++ if _, err := rand.Read(key); err != nil { ++ log.Fatal("failed to generate session key:", err) ++ } ++ store := sessions.NewCookieStore(key) ++ store.Options = &sessions.Options{ ++ Path: "/", ++ MaxAge: 86400, ++ HttpOnly: true, ++ Secure: true, ++ SameSite: http.SameSiteLaxMode, ++ } ++ return store ++} ++ ++// NewS3Client creates a minio client from user-provided credentials. ++func NewS3Client(endpoint, accessKey, secretKey string, useSSL, skipSSLVerify bool) (*minio.Client, error) { ++ opts := &minio.Options{ ++ Creds: credentials.NewStaticV4(accessKey, secretKey, ""), ++ Secure: useSSL, ++ } ++ if useSSL && skipSSLVerify { ++ opts.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} //nolint:gosec ++ } ++ return minio.New(endpoint, opts) ++} ++ ++// S3FromContext retrieves the S3 client stored in request context. ++func S3FromContext(ctx context.Context) S3 { ++ if s3, ok := ctx.Value(s3ContextKey).(S3); ok { ++ return s3 ++ } ++ return nil ++} ++ ++func contextWithS3(ctx context.Context, s3 S3) context.Context { ++ return context.WithValue(ctx, s3ContextKey, s3) ++} ++ ++// RequireAuth is middleware that validates session credentials and injects ++// an S3 client into the request context. Redirects to /login if no session. ++func RequireAuth(cfg *SessionConfig, next http.Handler) http.Handler { ++ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ++ session, _ := cfg.Store.Get(r, "s3session") ++ accessKey, ok1 := session.Values["accessKey"].(string) ++ secretKey, ok2 := session.Values["secretKey"].(string) ++ if !ok1 || !ok2 || accessKey == "" || secretKey == "" { ++ http.Redirect(w, r, "/login", http.StatusFound) ++ return ++ } ++ ++ s3, err := NewS3Client(cfg.Endpoint, accessKey, secretKey, cfg.UseSSL, cfg.SkipSSLVerify) ++ if err != nil { ++ // Session has bad credentials — clear and redirect to login ++ session.Options.MaxAge = -1 ++ _ = session.Save(r, w) ++ http.Redirect(w, r, "/login", http.StatusFound) ++ return ++ } ++ ++ ctx := contextWithS3(r.Context(), s3) ++ next.ServeHTTP(w, r.WithContext(ctx)) ++ }) ++} ++ ++// HandleLoginView renders the login page. ++func HandleLoginView(templates fs.FS) http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ errorMsg := r.URL.Query().Get("error") ++ ++ data := struct { ++ Error string ++ }{ ++ Error: errorMsg, ++ } ++ ++ t, err := template.ParseFS(templates, "layout.html.tmpl", "login.html.tmpl") ++ if err != nil { ++ handleHTTPError(w, fmt.Errorf("error parsing login template: %w", err)) ++ return ++ } ++ err = t.ExecuteTemplate(w, "layout", data) ++ if err != nil { ++ handleHTTPError(w, fmt.Errorf("error executing login template: %w", err)) ++ return ++ } ++ } ++} ++ ++// HandleLogin processes the login form POST. ++func HandleLogin(cfg *SessionConfig) http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ accessKey := r.FormValue("accessKey") ++ secretKey := r.FormValue("secretKey") ++ ++ if accessKey == "" || secretKey == "" { ++ http.Redirect(w, r, "/login?error=credentials+required", http.StatusFound) ++ return ++ } ++ ++ // Validate credentials by attempting ListBuckets ++ s3, err := NewS3Client(cfg.Endpoint, accessKey, secretKey, cfg.UseSSL, cfg.SkipSSLVerify) ++ if err != nil { ++ http.Redirect(w, r, "/login?error=connection+failed", http.StatusFound) ++ return ++ } ++ _, err = s3.ListBuckets(r.Context()) ++ if err != nil { ++ http.Redirect(w, r, "/login?error=invalid+credentials", http.StatusFound) ++ return ++ } ++ ++ // Save credentials to session ++ session, _ := cfg.Store.Get(r, "s3session") ++ session.Values["accessKey"] = accessKey ++ session.Values["secretKey"] = secretKey ++ err = session.Save(r, w) ++ if err != nil { ++ handleHTTPError(w, fmt.Errorf("error saving session: %w", err)) ++ return ++ } ++ ++ http.Redirect(w, r, "/buckets", http.StatusFound) ++ } ++} ++ ++// HandleLogout destroys the session and redirects to login. ++func HandleLogout(cfg *SessionConfig) http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ session, _ := cfg.Store.Get(r, "s3session") ++ session.Options.MaxAge = -1 ++ _ = session.Save(r, w) ++ http.Redirect(w, r, "/login", http.StatusFound) ++ } ++} ++ ++// Dynamic handler wrappers — extract S3 from context, delegate to original handlers. ++ ++// HandleBucketsViewDynamic wraps HandleBucketsView for login mode. ++func HandleBucketsViewDynamic(templates fs.FS, allowDelete bool) http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ s3 := S3FromContext(r.Context()) ++ HandleBucketsView(s3, templates, allowDelete).ServeHTTP(w, r) ++ } ++} ++ ++// HandleBucketViewDynamic wraps HandleBucketView for login mode. ++func HandleBucketViewDynamic(templates fs.FS, allowDelete bool, listRecursive bool) http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ s3 := S3FromContext(r.Context()) ++ HandleBucketView(s3, templates, allowDelete, listRecursive).ServeHTTP(w, r) ++ } ++} ++ ++// HandleCreateBucketDynamic wraps HandleCreateBucket for login mode. ++func HandleCreateBucketDynamic() http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ s3 := S3FromContext(r.Context()) ++ HandleCreateBucket(s3).ServeHTTP(w, r) ++ } ++} ++ ++// HandleDeleteBucketDynamic wraps HandleDeleteBucket for login mode. ++func HandleDeleteBucketDynamic() http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ s3 := S3FromContext(r.Context()) ++ HandleDeleteBucket(s3).ServeHTTP(w, r) ++ } ++} ++ ++// HandleCreateObjectDynamic wraps HandleCreateObject for login mode. ++func HandleCreateObjectDynamic(sseInfo SSEType) http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ s3 := S3FromContext(r.Context()) ++ HandleCreateObject(s3, sseInfo).ServeHTTP(w, r) ++ } ++} ++ ++// HandleGenerateUrlDynamic wraps HandleGenerateUrl for login mode. ++func HandleGenerateUrlDynamic() http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ s3 := S3FromContext(r.Context()) ++ HandleGenerateUrl(s3).ServeHTTP(w, r) ++ } ++} ++ ++// HandleGetObjectDynamic wraps HandleGetObject for login mode. ++func HandleGetObjectDynamic(forceDownload bool) http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ s3 := S3FromContext(r.Context()) ++ HandleGetObject(s3, forceDownload).ServeHTTP(w, r) ++ } ++} ++ ++// HandleDeleteObjectDynamic wraps HandleDeleteObject for login mode. ++func HandleDeleteObjectDynamic() http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ s3 := S3FromContext(r.Context()) ++ HandleDeleteObject(s3).ServeHTTP(w, r) ++ } ++} +diff --git a/web/template/login.html.tmpl b/web/template/login.html.tmpl +new file mode 100644 +index 0000000..f153018 +--- /dev/null ++++ b/web/template/login.html.tmpl +@@ -0,0 +1,46 @@ ++{{ define "content" }} ++ ++ ++
++
++
++
++
++
++ Sign In ++

Enter your S3 credentials to access the bucket manager.

++
++ ++ {{ if .Error }} ++
++ error {{ .Error }} ++
++ {{ end }} ++ ++
++
++ vpn_key ++ ++ ++
++
++ lock ++ ++ ++
++
++ ++ ++
++
++
++
++
++
++{{ end }} diff --git a/packages/system/bucket/templates/deployment.yaml b/packages/system/bucket/templates/deployment.yaml index 7e115de0..5c13cb2d 100644 --- a/packages/system/bucket/templates/deployment.yaml +++ b/packages/system/bucket/templates/deployment.yaml @@ -1,3 +1,12 @@ +{{- $endpoint := printf "s3.%s" .Values._namespace.host }} +{{- range $name, $user := .Values.users }} + {{- $secretName := printf "%s-%s" $.Values.bucketName $name }} + {{- $existingSecret := lookup "v1" "Secret" $.Release.Namespace $secretName }} + {{- if $existingSecret }} + {{- $bucketInfo := fromJson (b64dec (index $existingSecret.data "BucketInfo")) }} + {{- $endpoint = trimPrefix "https://" (index $bucketInfo.spec.secretS3 "endpoint") }} + {{- end }} +{{- end }} apiVersion: apps/v1 kind: Deployment metadata: @@ -17,19 +26,6 @@ spec: image: "{{ $.Files.Get "images/s3manager.tag" | trim }}" env: - name: ENDPOINT - valueFrom: - secretKeyRef: - name: {{ .Values.bucketName }}-credentials - key: endpoint + value: {{ $endpoint | quote }} - name: SKIP_SSL_VERIFICATION value: "true" - - name: ACCESS_KEY_ID - valueFrom: - secretKeyRef: - name: {{ .Values.bucketName }}-credentials - key: accessKey - - name: SECRET_ACCESS_KEY - valueFrom: - secretKeyRef: - name: {{ .Values.bucketName }}-credentials - key: secretKey diff --git a/packages/system/bucket/templates/ingress.yaml b/packages/system/bucket/templates/ingress.yaml index f7b4eed4..b7ffaf8b 100644 --- a/packages/system/bucket/templates/ingress.yaml +++ b/packages/system/bucket/templates/ingress.yaml @@ -1,22 +1,20 @@ {{- $host := .Values._namespace.host }} {{- $ingress := .Values._namespace.ingress }} -{{- $issuerType := (index .Values._cluster "clusterissuer") | default "http01" }} +{{- $solver := (index .Values._cluster "solver") | default "http01" }} +{{- $clusterIssuer := (index .Values._cluster "issuer-name") | default "letsencrypt-prod" }} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: {{ .Values.bucketName }}-ui annotations: - nginx.ingress.kubernetes.io/auth-type: "basic" - nginx.ingress.kubernetes.io/auth-secret: "{{ .Values.bucketName }}-ui-auth" - nginx.ingress.kubernetes.io/auth-realm: "Authentication Required" nginx.ingress.kubernetes.io/proxy-body-size: "0" nginx.ingress.kubernetes.io/proxy-read-timeout: "99999" nginx.ingress.kubernetes.io/proxy-send-timeout: "99999" - {{- if ne $issuerType "cloudflare" }} - acme.cert-manager.io/http01-ingress-class: {{ $ingress }} + {{- if eq $solver "http01" }} + acme.cert-manager.io/http01-ingress-ingressclassname: {{ $ingress }} {{- end }} - cert-manager.io/cluster-issuer: letsencrypt-prod + cert-manager.io/cluster-issuer: {{ $clusterIssuer }} spec: ingressClassName: {{ $ingress }} tls: diff --git a/packages/system/bucket/templates/secret.yaml b/packages/system/bucket/templates/secret.yaml index 15474569..683a0972 100644 --- a/packages/system/bucket/templates/secret.yaml +++ b/packages/system/bucket/templates/secret.yaml @@ -1,24 +1,2 @@ -{{- $existingSecret := lookup "v1" "Secret" .Release.Namespace .Values.bucketName }} -{{- $bucketInfo := fromJson (b64dec (index $existingSecret.data "BucketInfo")) }} -{{- $accessKeyID := index $bucketInfo.spec.secretS3 "accessKeyID" }} -{{- $accessSecretKey := index $bucketInfo.spec.secretS3 "accessSecretKey" }} -{{- $endpoint := index $bucketInfo.spec.secretS3 "endpoint" }} -{{- $bucketName := index $bucketInfo.spec "bucketName" }} - -apiVersion: v1 -kind: Secret -metadata: - name: {{ .Values.bucketName }}-credentials -type: Opaque -stringData: - accessKey: {{ $accessKeyID | quote }} - secretKey: {{ $accessSecretKey | quote }} - endpoint: {{ trimPrefix "https://" $endpoint }} - bucketName: {{ $bucketName | quote }} ---- -apiVersion: v1 -kind: Secret -metadata: - name: {{ .Values.bucketName }}-ui-auth -data: - auth: {{ htpasswd $accessKeyID $accessSecretKey | b64enc | quote }} +{{/* Secrets previously used for s3manager credential injection and nginx basic auth */}} +{{/* are no longer needed — s3manager now handles authentication via its own login page */}} diff --git a/packages/system/bucket/templates/user-credentials.yaml b/packages/system/bucket/templates/user-credentials.yaml new file mode 100644 index 00000000..7ccdd731 --- /dev/null +++ b/packages/system/bucket/templates/user-credentials.yaml @@ -0,0 +1,20 @@ +{{- range $name, $user := .Values.users }} +{{- $secretName := printf "%s-%s" $.Values.bucketName $name }} +{{- $existingSecret := lookup "v1" "Secret" $.Release.Namespace $secretName }} +{{- if $existingSecret }} +{{- $bucketInfo := fromJson (b64dec (index $existingSecret.data "BucketInfo")) }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secretName }}-credentials + labels: + apps.cozystack.io/user-secret: "true" +type: Opaque +stringData: + accessKey: {{ index $bucketInfo.spec.secretS3 "accessKeyID" | quote }} + secretKey: {{ index $bucketInfo.spec.secretS3 "accessSecretKey" | quote }} + endpoint: {{ trimPrefix "https://" (index $bucketInfo.spec.secretS3 "endpoint") }} + bucketName: {{ index $bucketInfo.spec "bucketName" | quote }} +{{- end }} +{{- end }} diff --git a/packages/system/bucket/values.yaml b/packages/system/bucket/values.yaml index e1499c6e..739c4977 100644 --- a/packages/system/bucket/values.yaml +++ b/packages/system/bucket/values.yaml @@ -1 +1,2 @@ bucketName: "cozystack" +users: {} diff --git a/packages/system/capi-providers-cpprovider/files/components.gz b/packages/system/capi-providers-cpprovider/files/components.gz index 60ac302a..930a26af 100644 Binary files a/packages/system/capi-providers-cpprovider/files/components.gz and b/packages/system/capi-providers-cpprovider/files/components.gz differ diff --git a/packages/system/capi-providers-cpprovider/files/control-plane-components.yaml b/packages/system/capi-providers-cpprovider/files/control-plane-components.yaml index d30e7373..64fc7d6d 100644 --- a/packages/system/capi-providers-cpprovider/files/control-plane-components.yaml +++ b/packages/system/capi-providers-cpprovider/files/control-plane-components.yaml @@ -109,7 +109,7 @@ spec: agent: default: image: registry.k8s.io/kas-network-proxy/proxy-agent - version: v0.28.6 + mode: DaemonSet properties: extraArgs: description: |- @@ -120,10 +120,31 @@ spec: items: type: string type: array + hostNetwork: + default: false + description: |- + HostNetwork enables the konnectivity agent to use the Host network namespace. + By enabling this mode, the Agent doesn't need to wait for the CNI initialisation, + enabling a sort of out-of-band access to nodes for troubleshooting scenarios, + or when the agent needs direct access to the host network. + type: boolean image: default: registry.k8s.io/kas-network-proxy/proxy-agent description: AgentImage defines the container image for Konnectivity's agent. type: string + mode: + default: DaemonSet + description: 'Mode allows specifying the Agent deployment mode: Deployment, or DaemonSet (default).' + enum: + - DaemonSet + - Deployment + type: string + replicas: + description: |- + Replicas defines the number of replicas when Mode is Deployment. + Must be 0 if Mode is DaemonSet. + format: int32 + type: integer tolerations: default: - key: CriticalAddonsOnly @@ -169,15 +190,20 @@ spec: type: object type: array version: - default: v0.28.6 - description: Version for Konnectivity agent. + description: |- + Version for Konnectivity agent. + If left empty, Kamaji will automatically inflect the version from the deployed Tenant Control Plane. + + WARNING: for last cut-off releases, the container image could be not available. type: string type: object + x-kubernetes-validations: + - message: replicas must be 0 when mode is DaemonSet, and greater than 0 when mode is Deployment + rule: '!(self.mode == ''DaemonSet'' && has(self.replicas) && self.replicas != 0) && !(self.mode == ''Deployment'' && self.replicas == 0)' server: default: image: registry.k8s.io/kas-network-proxy/proxy-server port: 8132 - version: v0.28.6 properties: extraArgs: description: |- @@ -204,7 +230,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This is an alpha field and requires enabling the + This field depends on the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -256,8 +282,11 @@ spec: type: object type: object version: - default: v0.28.6 - description: Container image version of the Konnectivity server. + description: |- + Container image version of the Konnectivity server. + If left empty, Kamaji will automatically inflect the version from the deployed Tenant Control Plane. + + WARNING: for last cut-off releases, the container image could be not available. type: string required: - port @@ -410,7 +439,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This is an alpha field and requires enabling the + This field depends on the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -560,7 +589,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This is an alpha field and requires enabling the + This field depends on the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -620,6 +649,16 @@ spec: dataStoreSchema: description: DataStoreSchema allows to specify the name of the database (for relational DataStores) or the key prefix (for etcd) type: string + dataStoreUsername: + description: |- + DataStoreUsername allows to specify the username of the database (for relational DataStores). This + value is optional and immutable. Note that Kamaji currently doesn't ensure that DataStoreUsername values are unique. It's up + to the user to avoid clashes between different TenantControlPlanes. If not set upon creation, Kamaji will default the + DataStoreUsername by concatenating the namespace and name of the TenantControlPlane. + type: string + x-kubernetes-validations: + - message: changing the dataStoreUsername is not supported + rule: self == oldSelf deployment: description: Configure how the TenantControlPlane Deployment object should be configured. properties: @@ -903,7 +942,6 @@ spec: pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both matchLabelKeys and labelSelector. Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array @@ -918,7 +956,6 @@ spec: pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array @@ -1079,7 +1116,6 @@ spec: pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both matchLabelKeys and labelSelector. Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array @@ -1094,7 +1130,6 @@ spec: pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array @@ -1183,8 +1218,8 @@ spec: most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + compute a sum by iterating through the elements of this field and subtracting + "weight" from the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred. items: description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) @@ -1248,7 +1283,6 @@ spec: pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both matchLabelKeys and labelSelector. Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array @@ -1263,7 +1297,6 @@ spec: pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array @@ -1424,7 +1457,6 @@ spec: pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both matchLabelKeys and labelSelector. Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array @@ -1439,7 +1471,6 @@ spec: pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array @@ -1587,7 +1618,9 @@ spec: description: EnvVar represents an environment variable present in a Container. properties: name: - description: Name of the environment variable. Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -1641,6 +1674,42 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -1696,13 +1765,13 @@ spec: envFrom: description: |- List of sources to populate environment variables in the container. - The keys defined within a source must be a C_IDENTIFIER. All invalid keys - will be reported as an event when the container is starting. When a key exists in multiple + The keys defined within a source may consist of any printable ASCII characters except '='. + When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated. items: - description: EnvFromSource represents the source of a set of ConfigMaps + description: EnvFromSource represents the source of a set of ConfigMaps or Secrets properties: configMapRef: description: The ConfigMap to select from @@ -1722,7 +1791,9 @@ spec: type: object x-kubernetes-map-type: atomic prefix: - description: An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER. + description: |- + Optional text to prepend to the name of each environment variable. + May consist of any printable ASCII characters except '='. type: string secretRef: description: The Secret to select from @@ -1971,6 +2042,12 @@ spec: - port type: object type: object + stopSignal: + description: |- + StopSignal defines which signal will be sent to a container when it is being stopped. + If not specified, the default is defined by the container runtime in use. + StopSignal can only be set for Pods with a non-empty .spec.os.name + type: string type: object livenessProbe: description: |- @@ -2361,7 +2438,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This is an alpha field and requires enabling the + This field depends on the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -2415,10 +2492,10 @@ spec: restartPolicy: description: |- RestartPolicy defines the restart behavior of individual containers in a pod. - This field may only be set for init containers, and the only allowed value is "Always". - For non-init containers or when this field is not specified, + This overrides the pod-level restart policy. When this field is not specified, the restart behavior is defined by the Pod's restart policy and the container type. - Setting the RestartPolicy as "Always" for the init container will have the following effect: + Additionally, setting the RestartPolicy as "Always" for the init container will + have the following effect: this init container will be continually restarted on exit until all regular containers have terminated. Once all regular containers have completed, all init containers with restartPolicy "Always" @@ -2430,6 +2507,57 @@ spec: init container is started, or after any startupProbe has successfully completed. type: string + restartPolicyRules: + description: |- + Represents a list of rules to be checked to determine if the + container should be restarted on exit. The rules are evaluated in + order. Once a rule matches a container exit condition, the remaining + rules are ignored. If no rule matches the container exit condition, + the Container-level restart policy determines the whether the container + is restarted or not. Constraints on the rules: + - At most 20 rules are allowed. + - Rules can have the same action. + - Identical rules are not forbidden in validations. + When rules are specified, container MUST set RestartPolicy explicitly + even it if matches the Pod's RestartPolicy. + items: + description: ContainerRestartRule describes how a container exit is handled. + properties: + action: + description: |- + Specifies the action taken on a container exit if the requirements + are satisfied. The only possible value is "Restart" to restart the + container. + type: string + exitCodes: + description: Represents the exit codes to check on container exits. + properties: + operator: + description: |- + Represents the relationship between the container exit code(s) and the + specified values. Possible values are: + - In: the requirement is satisfied if the container exit code is in the + set of specified values. + - NotIn: the requirement is satisfied if the container exit code is + not in the set of specified values. + type: string + values: + description: |- + Specifies the set of values to check for container exit codes. + At most 255 elements are allowed. + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic securityContext: description: |- SecurityContext defines the security options the container should be run with. @@ -2951,7 +3079,9 @@ spec: description: EnvVar represents an environment variable present in a Container. properties: name: - description: Name of the environment variable. Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -3005,6 +3135,42 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -3060,13 +3226,13 @@ spec: envFrom: description: |- List of sources to populate environment variables in the container. - The keys defined within a source must be a C_IDENTIFIER. All invalid keys - will be reported as an event when the container is starting. When a key exists in multiple + The keys defined within a source may consist of any printable ASCII characters except '='. + When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated. items: - description: EnvFromSource represents the source of a set of ConfigMaps + description: EnvFromSource represents the source of a set of ConfigMaps or Secrets properties: configMapRef: description: The ConfigMap to select from @@ -3086,7 +3252,9 @@ spec: type: object x-kubernetes-map-type: atomic prefix: - description: An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER. + description: |- + Optional text to prepend to the name of each environment variable. + May consist of any printable ASCII characters except '='. type: string secretRef: description: The Secret to select from @@ -3335,6 +3503,12 @@ spec: - port type: object type: object + stopSignal: + description: |- + StopSignal defines which signal will be sent to a container when it is being stopped. + If not specified, the default is defined by the container runtime in use. + StopSignal can only be set for Pods with a non-empty .spec.os.name + type: string type: object livenessProbe: description: |- @@ -3725,7 +3899,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This is an alpha field and requires enabling the + This field depends on the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -3779,10 +3953,10 @@ spec: restartPolicy: description: |- RestartPolicy defines the restart behavior of individual containers in a pod. - This field may only be set for init containers, and the only allowed value is "Always". - For non-init containers or when this field is not specified, + This overrides the pod-level restart policy. When this field is not specified, the restart behavior is defined by the Pod's restart policy and the container type. - Setting the RestartPolicy as "Always" for the init container will have the following effect: + Additionally, setting the RestartPolicy as "Always" for the init container will + have the following effect: this init container will be continually restarted on exit until all regular containers have terminated. Once all regular containers have completed, all init containers with restartPolicy "Always" @@ -3794,6 +3968,57 @@ spec: init container is started, or after any startupProbe has successfully completed. type: string + restartPolicyRules: + description: |- + Represents a list of rules to be checked to determine if the + container should be restarted on exit. The rules are evaluated in + order. Once a rule matches a container exit condition, the remaining + rules are ignored. If no rule matches the container exit condition, + the Container-level restart policy determines the whether the container + is restarted or not. Constraints on the rules: + - At most 20 rules are allowed. + - Rules can have the same action. + - Identical rules are not forbidden in validations. + When rules are specified, container MUST set RestartPolicy explicitly + even it if matches the Pod's RestartPolicy. + items: + description: ContainerRestartRule describes how a container exit is handled. + properties: + action: + description: |- + Specifies the action taken on a container exit if the requirements + are satisfied. The only possible value is "Restart" to restart the + container. + type: string + exitCodes: + description: Represents the exit codes to check on container exits. + properties: + operator: + description: |- + Represents the relationship between the container exit code(s) and the + specified values. Possible values are: + - In: the requirement is satisfied if the container exit code is in the + set of specified values. + - NotIn: the requirement is satisfied if the container exit code is + not in the set of specified values. + type: string + values: + description: |- + Specifies the set of values to check for container exit codes. + At most 255 elements are allowed. + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic securityContext: description: |- SecurityContext defines the security options the container should be run with. @@ -4915,15 +5140,13 @@ spec: volumeAttributesClassName may be used to set the VolumeAttributesClass used by this claim. If specified, the CSI driver will create or update the volume with the attributes defined in the corresponding VolumeAttributesClass. This has a different purpose than storageClassName, - it can be changed after the claim is created. An empty string value means that no VolumeAttributesClass - will be applied to the claim but it's not allowed to reset this field to empty string once it is set. - If unspecified and the PersistentVolumeClaim is unbound, the default VolumeAttributesClass - will be set by the persistentvolume controller if it exists. + it can be changed after the claim is created. An empty string or nil value indicates that no + VolumeAttributesClass will be applied to the claim. If the claim enters an Infeasible error state, + this field can be reset to its previous value (including nil) to cancel the modification. If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource exists. More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/ - (Beta) Using this field requires the VolumeAttributesClass feature gate to be enabled (off by default). type: string volumeMode: description: |- @@ -5097,12 +5320,9 @@ spec: description: |- glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. Deprecated: Glusterfs is deprecated and the in-tree glusterfs type is no longer supported. - More info: https://examples.k8s.io/volumes/glusterfs/README.md properties: endpoints: - description: |- - endpoints is the endpoint name that details Glusterfs topology. - More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod + description: endpoints is the endpoint name that details Glusterfs topology. type: string path: description: |- @@ -5156,7 +5376,7 @@ spec: The types of objects that may be mounted by this volume are defined by the container runtime implementation on a host machine and at minimum must include all valid types supported by the container image field. The OCI object gets mounted in a single directory (spec.containers[*].volumeMounts.mountPath) by merging the manifest layers in the same way as for container images. The volume will be mounted read-only (ro) and non-executable files (noexec). - Sub path mounts for containers are not supported (spec.containers[*].volumeMounts.subpath). + Sub path mounts for containers are not supported (spec.containers[*].volumeMounts.subpath) before 1.33. The field spec.securityContext.fsGroupChangePolicy has no effect on this volume type. properties: pullPolicy: @@ -5181,7 +5401,7 @@ spec: description: |- iscsi represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. - More info: https://examples.k8s.io/volumes/iscsi/README.md + More info: https://kubernetes.io/docs/concepts/storage/volumes/#iscsi properties: chapAuthDiscovery: description: chapAuthDiscovery defines whether support iSCSI Discovery CHAP authentication @@ -5571,6 +5791,110 @@ spec: type: array x-kubernetes-list-type: atomic type: object + podCertificate: + description: |- + Projects an auto-rotating credential bundle (private key and certificate + chain) that the pod can use either as a TLS client or server. + + Kubelet generates a private key and uses it to send a + PodCertificateRequest to the named signer. Once the signer approves the + request and issues a certificate chain, Kubelet writes the key and + certificate chain to the pod filesystem. The pod does not start until + certificates have been issued for each podCertificate projected volume + source in its spec. + + Kubelet will begin trying to rotate the certificate at the time indicated + by the signer using the PodCertificateRequest.Status.BeginRefreshAt + timestamp. + + Kubelet can write a single file, indicated by the credentialBundlePath + field, or separate files, indicated by the keyPath and + certificateChainPath fields. + + The credential bundle is a single file in PEM format. The first PEM + entry is the private key (in PKCS#8 format), and the remaining PEM + entries are the certificate chain issued by the signer (typically, + signers will return their certificate chain in leaf-to-root order). + + Prefer using the credential bundle format, since your application code + can read it atomically. If you use keyPath and certificateChainPath, + your application must make two separate file reads. If these coincide + with a certificate rotation, it is possible that the private key and leaf + certificate you read may not correspond to each other. Your application + will need to check for this condition, and re-read until they are + consistent. + + The named signer controls chooses the format of the certificate it + issues; consult the signer implementation's documentation to learn how to + use the certificates it issues. + properties: + certificateChainPath: + description: |- + Write the certificate chain at this path in the projected volume. + + Most applications should use credentialBundlePath. When using keyPath + and certificateChainPath, your application needs to check that the key + and leaf certificate are consistent, because it is possible to read the + files mid-rotation. + type: string + credentialBundlePath: + description: |- + Write the credential bundle at this path in the projected volume. + + The credential bundle is a single file that contains multiple PEM blocks. + The first PEM block is a PRIVATE KEY block, containing a PKCS#8 private + key. + + The remaining blocks are CERTIFICATE blocks, containing the issued + certificate chain from the signer (leaf and any intermediates). + + Using credentialBundlePath lets your Pod's application code make a single + atomic read that retrieves a consistent key and certificate chain. If you + project them to separate files, your application code will need to + additionally check that the leaf certificate was issued to the key. + type: string + keyPath: + description: |- + Write the key at this path in the projected volume. + + Most applications should use credentialBundlePath. When using keyPath + and certificateChainPath, your application needs to check that the key + and leaf certificate are consistent, because it is possible to read the + files mid-rotation. + type: string + keyType: + description: |- + The type of keypair Kubelet will generate for the pod. + + Valid values are "RSA3072", "RSA4096", "ECDSAP256", "ECDSAP384", + "ECDSAP521", and "ED25519". + type: string + maxExpirationSeconds: + description: |- + maxExpirationSeconds is the maximum lifetime permitted for the + certificate. + + Kubelet copies this value verbatim into the PodCertificateRequests it + generates for this projection. + + If omitted, kube-apiserver will set it to 86400(24 hours). kube-apiserver + will reject values shorter than 3600 (1 hour). The maximum allowable + value is 7862400 (91 days). + + The signer implementation is then free to issue a certificate with any + lifetime *shorter* than MaxExpirationSeconds, but no shorter than 3600 + seconds (1 hour). This constraint is enforced by kube-apiserver. + `kubernetes.io` signers will never issue certificates with a lifetime + longer than 24 hours. + format: int32 + type: integer + signerName: + description: Kubelet's generated CSRs will be addressed to this signer. + type: string + required: + - keyType + - signerName + type: object secret: description: secret information about the secret data to project properties: @@ -5700,7 +6024,6 @@ spec: description: |- rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. Deprecated: RBD is deprecated and the in-tree rbd type is no longer supported. - More info: https://examples.k8s.io/volumes/rbd/README.md properties: fsType: description: |- @@ -6199,7 +6522,6 @@ spec: - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations. If this value is nil, the behavior is equivalent to the Honor policy. - This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag. type: string nodeTaintsPolicy: description: |- @@ -6210,7 +6532,6 @@ spec: - Ignore: node taints are ignored. All nodes are included. If this value is nil, the behavior is equivalent to the Ignore policy. - This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag. type: string topologyKey: description: |- @@ -6270,7 +6591,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This is an alpha field and requires enabling the + This field depends on the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -6326,14 +6647,14 @@ spec: default: cgroupfs: systemd preferredAddressTypes: - - Hostname - InternalIP - ExternalIP + - Hostname description: Configure the Kubelet options, such as the preferred address types, or the expected cgroupfs. properties: cgroupfs: description: |- - CGroupFS defines the cgroup driver for Kubelet + CGroupFS defines the cgroup driver for Kubelet https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/configure-cgroup-driver/ enum: - systemd @@ -6341,12 +6662,12 @@ spec: type: string preferredAddressTypes: default: - - Hostname - InternalIP - ExternalIP + - Hostname description: |- Ordered list of the preferred NodeAddressTypes to use for kubelet connections. - Default to Hostname, InternalIP, ExternalIP. + Default to InternalIP, ExternalIP, Hostname. items: enum: - Hostname @@ -6357,6 +6678,7 @@ spec: type: string minItems: 1 type: array + x-kubernetes-list-type: set type: object network: default: @@ -6557,7 +6879,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This is an alpha field and requires enabling the + This field depends on the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -6844,7 +7166,7 @@ spec: agent: default: image: registry.k8s.io/kas-network-proxy/proxy-agent - version: v0.28.6 + mode: DaemonSet properties: extraArgs: description: |- @@ -6855,10 +7177,31 @@ spec: items: type: string type: array + hostNetwork: + default: false + description: |- + HostNetwork enables the konnectivity agent to use the Host network namespace. + By enabling this mode, the Agent doesn't need to wait for the CNI initialisation, + enabling a sort of out-of-band access to nodes for troubleshooting scenarios, + or when the agent needs direct access to the host network. + type: boolean image: default: registry.k8s.io/kas-network-proxy/proxy-agent description: AgentImage defines the container image for Konnectivity's agent. type: string + mode: + default: DaemonSet + description: 'Mode allows specifying the Agent deployment mode: Deployment, or DaemonSet (default).' + enum: + - DaemonSet + - Deployment + type: string + replicas: + description: |- + Replicas defines the number of replicas when Mode is Deployment. + Must be 0 if Mode is DaemonSet. + format: int32 + type: integer tolerations: default: - key: CriticalAddonsOnly @@ -6904,15 +7247,20 @@ spec: type: object type: array version: - default: v0.28.6 - description: Version for Konnectivity agent. + description: |- + Version for Konnectivity agent. + If left empty, Kamaji will automatically inflect the version from the deployed Tenant Control Plane. + + WARNING: for last cut-off releases, the container image could be not available. type: string type: object + x-kubernetes-validations: + - message: replicas must be 0 when mode is DaemonSet, and greater than 0 when mode is Deployment + rule: '!(self.mode == ''DaemonSet'' && has(self.replicas) && self.replicas != 0) && !(self.mode == ''Deployment'' && self.replicas == 0)' server: default: image: registry.k8s.io/kas-network-proxy/proxy-server port: 8132 - version: v0.28.6 properties: extraArgs: description: |- @@ -6939,7 +7287,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This is an alpha field and requires enabling the + This field depends on the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -6991,8 +7339,11 @@ spec: type: object type: object version: - default: v0.28.6 - description: Container image version of the Konnectivity server. + description: |- + Container image version of the Konnectivity server. + If left empty, Kamaji will automatically inflect the version from the deployed Tenant Control Plane. + + WARNING: for last cut-off releases, the container image could be not available. type: string required: - port @@ -7145,7 +7496,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This is an alpha field and requires enabling the + This field depends on the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -7280,7 +7631,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This is an alpha field and requires enabling the + This field depends on the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -7340,6 +7691,16 @@ spec: dataStoreSchema: description: DataStoreSchema allows to specify the name of the database (for relational DataStores) or the key prefix (for etcd) type: string + dataStoreUsername: + description: |- + DataStoreUsername allows to specify the username of the database (for relational DataStores). This + value is optional and immutable. Note that Kamaji currently doesn't ensure that DataStoreUsername values are unique. It's up + to the user to avoid clashes between different TenantControlPlanes. If not set upon creation, Kamaji will default the + DataStoreUsername by concatenating the namespace and name of the TenantControlPlane. + type: string + x-kubernetes-validations: + - message: changing the dataStoreUsername is not supported + rule: self == oldSelf deployment: description: Configure how the TenantControlPlane Deployment object should be configured. properties: @@ -7623,7 +7984,6 @@ spec: pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both matchLabelKeys and labelSelector. Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array @@ -7638,7 +7998,6 @@ spec: pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array @@ -7799,7 +8158,6 @@ spec: pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both matchLabelKeys and labelSelector. Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array @@ -7814,7 +8172,6 @@ spec: pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array @@ -7903,8 +8260,8 @@ spec: most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + compute a sum by iterating through the elements of this field and subtracting + "weight" from the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred. items: description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) @@ -7968,7 +8325,6 @@ spec: pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both matchLabelKeys and labelSelector. Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array @@ -7983,7 +8339,6 @@ spec: pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array @@ -8144,7 +8499,6 @@ spec: pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both matchLabelKeys and labelSelector. Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array @@ -8159,7 +8513,6 @@ spec: pod labels will be ignored. The default value is empty. The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). items: type: string type: array @@ -8307,7 +8660,9 @@ spec: description: EnvVar represents an environment variable present in a Container. properties: name: - description: Name of the environment variable. Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -8361,6 +8716,42 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -8416,13 +8807,13 @@ spec: envFrom: description: |- List of sources to populate environment variables in the container. - The keys defined within a source must be a C_IDENTIFIER. All invalid keys - will be reported as an event when the container is starting. When a key exists in multiple + The keys defined within a source may consist of any printable ASCII characters except '='. + When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated. items: - description: EnvFromSource represents the source of a set of ConfigMaps + description: EnvFromSource represents the source of a set of ConfigMaps or Secrets properties: configMapRef: description: The ConfigMap to select from @@ -8442,7 +8833,9 @@ spec: type: object x-kubernetes-map-type: atomic prefix: - description: An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER. + description: |- + Optional text to prepend to the name of each environment variable. + May consist of any printable ASCII characters except '='. type: string secretRef: description: The Secret to select from @@ -8691,6 +9084,12 @@ spec: - port type: object type: object + stopSignal: + description: |- + StopSignal defines which signal will be sent to a container when it is being stopped. + If not specified, the default is defined by the container runtime in use. + StopSignal can only be set for Pods with a non-empty .spec.os.name + type: string type: object livenessProbe: description: |- @@ -9081,7 +9480,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This is an alpha field and requires enabling the + This field depends on the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -9135,10 +9534,10 @@ spec: restartPolicy: description: |- RestartPolicy defines the restart behavior of individual containers in a pod. - This field may only be set for init containers, and the only allowed value is "Always". - For non-init containers or when this field is not specified, + This overrides the pod-level restart policy. When this field is not specified, the restart behavior is defined by the Pod's restart policy and the container type. - Setting the RestartPolicy as "Always" for the init container will have the following effect: + Additionally, setting the RestartPolicy as "Always" for the init container will + have the following effect: this init container will be continually restarted on exit until all regular containers have terminated. Once all regular containers have completed, all init containers with restartPolicy "Always" @@ -9150,6 +9549,57 @@ spec: init container is started, or after any startupProbe has successfully completed. type: string + restartPolicyRules: + description: |- + Represents a list of rules to be checked to determine if the + container should be restarted on exit. The rules are evaluated in + order. Once a rule matches a container exit condition, the remaining + rules are ignored. If no rule matches the container exit condition, + the Container-level restart policy determines the whether the container + is restarted or not. Constraints on the rules: + - At most 20 rules are allowed. + - Rules can have the same action. + - Identical rules are not forbidden in validations. + When rules are specified, container MUST set RestartPolicy explicitly + even it if matches the Pod's RestartPolicy. + items: + description: ContainerRestartRule describes how a container exit is handled. + properties: + action: + description: |- + Specifies the action taken on a container exit if the requirements + are satisfied. The only possible value is "Restart" to restart the + container. + type: string + exitCodes: + description: Represents the exit codes to check on container exits. + properties: + operator: + description: |- + Represents the relationship between the container exit code(s) and the + specified values. Possible values are: + - In: the requirement is satisfied if the container exit code is in the + set of specified values. + - NotIn: the requirement is satisfied if the container exit code is + not in the set of specified values. + type: string + values: + description: |- + Specifies the set of values to check for container exit codes. + At most 255 elements are allowed. + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic securityContext: description: |- SecurityContext defines the security options the container should be run with. @@ -9671,7 +10121,9 @@ spec: description: EnvVar represents an environment variable present in a Container. properties: name: - description: Name of the environment variable. Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -9725,6 +10177,42 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -9780,13 +10268,13 @@ spec: envFrom: description: |- List of sources to populate environment variables in the container. - The keys defined within a source must be a C_IDENTIFIER. All invalid keys - will be reported as an event when the container is starting. When a key exists in multiple + The keys defined within a source may consist of any printable ASCII characters except '='. + When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated. items: - description: EnvFromSource represents the source of a set of ConfigMaps + description: EnvFromSource represents the source of a set of ConfigMaps or Secrets properties: configMapRef: description: The ConfigMap to select from @@ -9806,7 +10294,9 @@ spec: type: object x-kubernetes-map-type: atomic prefix: - description: An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER. + description: |- + Optional text to prepend to the name of each environment variable. + May consist of any printable ASCII characters except '='. type: string secretRef: description: The Secret to select from @@ -10055,6 +10545,12 @@ spec: - port type: object type: object + stopSignal: + description: |- + StopSignal defines which signal will be sent to a container when it is being stopped. + If not specified, the default is defined by the container runtime in use. + StopSignal can only be set for Pods with a non-empty .spec.os.name + type: string type: object livenessProbe: description: |- @@ -10445,7 +10941,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This is an alpha field and requires enabling the + This field depends on the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -10499,10 +10995,10 @@ spec: restartPolicy: description: |- RestartPolicy defines the restart behavior of individual containers in a pod. - This field may only be set for init containers, and the only allowed value is "Always". - For non-init containers or when this field is not specified, + This overrides the pod-level restart policy. When this field is not specified, the restart behavior is defined by the Pod's restart policy and the container type. - Setting the RestartPolicy as "Always" for the init container will have the following effect: + Additionally, setting the RestartPolicy as "Always" for the init container will + have the following effect: this init container will be continually restarted on exit until all regular containers have terminated. Once all regular containers have completed, all init containers with restartPolicy "Always" @@ -10514,6 +11010,57 @@ spec: init container is started, or after any startupProbe has successfully completed. type: string + restartPolicyRules: + description: |- + Represents a list of rules to be checked to determine if the + container should be restarted on exit. The rules are evaluated in + order. Once a rule matches a container exit condition, the remaining + rules are ignored. If no rule matches the container exit condition, + the Container-level restart policy determines the whether the container + is restarted or not. Constraints on the rules: + - At most 20 rules are allowed. + - Rules can have the same action. + - Identical rules are not forbidden in validations. + When rules are specified, container MUST set RestartPolicy explicitly + even it if matches the Pod's RestartPolicy. + items: + description: ContainerRestartRule describes how a container exit is handled. + properties: + action: + description: |- + Specifies the action taken on a container exit if the requirements + are satisfied. The only possible value is "Restart" to restart the + container. + type: string + exitCodes: + description: Represents the exit codes to check on container exits. + properties: + operator: + description: |- + Represents the relationship between the container exit code(s) and the + specified values. Possible values are: + - In: the requirement is satisfied if the container exit code is in the + set of specified values. + - NotIn: the requirement is satisfied if the container exit code is + not in the set of specified values. + type: string + values: + description: |- + Specifies the set of values to check for container exit codes. + At most 255 elements are allowed. + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic securityContext: description: |- SecurityContext defines the security options the container should be run with. @@ -11635,15 +12182,13 @@ spec: volumeAttributesClassName may be used to set the VolumeAttributesClass used by this claim. If specified, the CSI driver will create or update the volume with the attributes defined in the corresponding VolumeAttributesClass. This has a different purpose than storageClassName, - it can be changed after the claim is created. An empty string value means that no VolumeAttributesClass - will be applied to the claim but it's not allowed to reset this field to empty string once it is set. - If unspecified and the PersistentVolumeClaim is unbound, the default VolumeAttributesClass - will be set by the persistentvolume controller if it exists. + it can be changed after the claim is created. An empty string or nil value indicates that no + VolumeAttributesClass will be applied to the claim. If the claim enters an Infeasible error state, + this field can be reset to its previous value (including nil) to cancel the modification. If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource exists. More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/ - (Beta) Using this field requires the VolumeAttributesClass feature gate to be enabled (off by default). type: string volumeMode: description: |- @@ -11817,12 +12362,9 @@ spec: description: |- glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. Deprecated: Glusterfs is deprecated and the in-tree glusterfs type is no longer supported. - More info: https://examples.k8s.io/volumes/glusterfs/README.md properties: endpoints: - description: |- - endpoints is the endpoint name that details Glusterfs topology. - More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod + description: endpoints is the endpoint name that details Glusterfs topology. type: string path: description: |- @@ -11876,7 +12418,7 @@ spec: The types of objects that may be mounted by this volume are defined by the container runtime implementation on a host machine and at minimum must include all valid types supported by the container image field. The OCI object gets mounted in a single directory (spec.containers[*].volumeMounts.mountPath) by merging the manifest layers in the same way as for container images. The volume will be mounted read-only (ro) and non-executable files (noexec). - Sub path mounts for containers are not supported (spec.containers[*].volumeMounts.subpath). + Sub path mounts for containers are not supported (spec.containers[*].volumeMounts.subpath) before 1.33. The field spec.securityContext.fsGroupChangePolicy has no effect on this volume type. properties: pullPolicy: @@ -11901,7 +12443,7 @@ spec: description: |- iscsi represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. - More info: https://examples.k8s.io/volumes/iscsi/README.md + More info: https://kubernetes.io/docs/concepts/storage/volumes/#iscsi properties: chapAuthDiscovery: description: chapAuthDiscovery defines whether support iSCSI Discovery CHAP authentication @@ -12291,6 +12833,110 @@ spec: type: array x-kubernetes-list-type: atomic type: object + podCertificate: + description: |- + Projects an auto-rotating credential bundle (private key and certificate + chain) that the pod can use either as a TLS client or server. + + Kubelet generates a private key and uses it to send a + PodCertificateRequest to the named signer. Once the signer approves the + request and issues a certificate chain, Kubelet writes the key and + certificate chain to the pod filesystem. The pod does not start until + certificates have been issued for each podCertificate projected volume + source in its spec. + + Kubelet will begin trying to rotate the certificate at the time indicated + by the signer using the PodCertificateRequest.Status.BeginRefreshAt + timestamp. + + Kubelet can write a single file, indicated by the credentialBundlePath + field, or separate files, indicated by the keyPath and + certificateChainPath fields. + + The credential bundle is a single file in PEM format. The first PEM + entry is the private key (in PKCS#8 format), and the remaining PEM + entries are the certificate chain issued by the signer (typically, + signers will return their certificate chain in leaf-to-root order). + + Prefer using the credential bundle format, since your application code + can read it atomically. If you use keyPath and certificateChainPath, + your application must make two separate file reads. If these coincide + with a certificate rotation, it is possible that the private key and leaf + certificate you read may not correspond to each other. Your application + will need to check for this condition, and re-read until they are + consistent. + + The named signer controls chooses the format of the certificate it + issues; consult the signer implementation's documentation to learn how to + use the certificates it issues. + properties: + certificateChainPath: + description: |- + Write the certificate chain at this path in the projected volume. + + Most applications should use credentialBundlePath. When using keyPath + and certificateChainPath, your application needs to check that the key + and leaf certificate are consistent, because it is possible to read the + files mid-rotation. + type: string + credentialBundlePath: + description: |- + Write the credential bundle at this path in the projected volume. + + The credential bundle is a single file that contains multiple PEM blocks. + The first PEM block is a PRIVATE KEY block, containing a PKCS#8 private + key. + + The remaining blocks are CERTIFICATE blocks, containing the issued + certificate chain from the signer (leaf and any intermediates). + + Using credentialBundlePath lets your Pod's application code make a single + atomic read that retrieves a consistent key and certificate chain. If you + project them to separate files, your application code will need to + additionally check that the leaf certificate was issued to the key. + type: string + keyPath: + description: |- + Write the key at this path in the projected volume. + + Most applications should use credentialBundlePath. When using keyPath + and certificateChainPath, your application needs to check that the key + and leaf certificate are consistent, because it is possible to read the + files mid-rotation. + type: string + keyType: + description: |- + The type of keypair Kubelet will generate for the pod. + + Valid values are "RSA3072", "RSA4096", "ECDSAP256", "ECDSAP384", + "ECDSAP521", and "ED25519". + type: string + maxExpirationSeconds: + description: |- + maxExpirationSeconds is the maximum lifetime permitted for the + certificate. + + Kubelet copies this value verbatim into the PodCertificateRequests it + generates for this projection. + + If omitted, kube-apiserver will set it to 86400(24 hours). kube-apiserver + will reject values shorter than 3600 (1 hour). The maximum allowable + value is 7862400 (91 days). + + The signer implementation is then free to issue a certificate with any + lifetime *shorter* than MaxExpirationSeconds, but no shorter than 3600 + seconds (1 hour). This constraint is enforced by kube-apiserver. + `kubernetes.io` signers will never issue certificates with a lifetime + longer than 24 hours. + format: int32 + type: integer + signerName: + description: Kubelet's generated CSRs will be addressed to this signer. + type: string + required: + - keyType + - signerName + type: object secret: description: secret information about the secret data to project properties: @@ -12420,7 +13066,6 @@ spec: description: |- rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. Deprecated: RBD is deprecated and the in-tree rbd type is no longer supported. - More info: https://examples.k8s.io/volumes/rbd/README.md properties: fsType: description: |- @@ -12919,7 +13564,6 @@ spec: - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations. If this value is nil, the behavior is equivalent to the Honor policy. - This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag. type: string nodeTaintsPolicy: description: |- @@ -12930,7 +13574,6 @@ spec: - Ignore: node taints are ignored. All nodes are included. If this value is nil, the behavior is equivalent to the Ignore policy. - This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag. type: string topologyKey: description: |- @@ -12990,7 +13633,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This is an alpha field and requires enabling the + This field depends on the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -13046,14 +13689,14 @@ spec: default: cgroupfs: systemd preferredAddressTypes: - - Hostname - InternalIP - ExternalIP + - Hostname description: Configure the Kubelet options, such as the preferred address types, or the expected cgroupfs. properties: cgroupfs: description: |- - CGroupFS defines the cgroup driver for Kubelet + CGroupFS defines the cgroup driver for Kubelet https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/configure-cgroup-driver/ enum: - systemd @@ -13061,12 +13704,12 @@ spec: type: string preferredAddressTypes: default: - - Hostname - InternalIP - ExternalIP + - Hostname description: |- Ordered list of the preferred NodeAddressTypes to use for kubelet connections. - Default to Hostname, InternalIP, ExternalIP. + Default to InternalIP, ExternalIP, Hostname. items: enum: - Hostname @@ -13077,6 +13720,7 @@ spec: type: string minItems: 1 type: array + x-kubernetes-list-type: set type: object network: default: @@ -13270,7 +13914,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This is an alpha field and requires enabling the + This field depends on the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -13516,6 +14160,8 @@ rules: - metal3clusters verbs: - get + - list + - watch - apiGroups: - kamaji.clastix.io resources: @@ -13626,14 +14272,14 @@ spec: - --dynamic-infrastructure-clusters=${CACPPK_INFRASTRUCTURE_CLUSTERS:= } command: - /manager - image: docker.io/clastix/cluster-api-control-plane-provider-kamaji:v0.15.0 + image: docker.io/clastix/cluster-api-control-plane-provider-kamaji:v0.16.0 livenessProbe: httpGet: path: /healthz port: 8081 initialDelaySeconds: 15 periodSeconds: 20 - name: manager + name: controller readinessProbe: httpGet: path: /readyz diff --git a/packages/system/capi-providers-cpprovider/files/metadata.yaml b/packages/system/capi-providers-cpprovider/files/metadata.yaml index b282734a..d22c9ab0 100644 --- a/packages/system/capi-providers-cpprovider/files/metadata.yaml +++ b/packages/system/capi-providers-cpprovider/files/metadata.yaml @@ -5,6 +5,9 @@ # update this file only when a new major or minor version is released apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3 releaseSeries: + - major: 0 + minor: 16 + contract: v1beta1 - major: 0 minor: 15 contract: v1beta1 diff --git a/packages/system/capi-providers-cpprovider/templates/configmaps.yaml b/packages/system/capi-providers-cpprovider/templates/configmaps.yaml index d69ab12b..736c06a5 100644 --- a/packages/system/capi-providers-cpprovider/templates/configmaps.yaml +++ b/packages/system/capi-providers-cpprovider/templates/configmaps.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: ConfigMap metadata: - name: v0.15.1-cp + name: v0.16.0-cp labels: cp-components: cozy annotations: diff --git a/packages/system/capi-providers-cpprovider/templates/providers.yaml b/packages/system/capi-providers-cpprovider/templates/providers.yaml index b173be14..125a82c7 100644 --- a/packages/system/capi-providers-cpprovider/templates/providers.yaml +++ b/packages/system/capi-providers-cpprovider/templates/providers.yaml @@ -4,7 +4,7 @@ metadata: name: kamaji spec: # https://github.com/clastix/cluster-api-control-plane-provider-kamaji - version: v0.15.1-cp + version: v0.16.0-cp fetchConfig: selector: matchLabels: diff --git a/packages/system/cert-manager-crds/Makefile b/packages/system/cert-manager-crds/Makefile index 48e5644c..7e7370ac 100644 --- a/packages/system/cert-manager-crds/Makefile +++ b/packages/system/cert-manager-crds/Makefile @@ -1,8 +1,5 @@ +export NAME=cert-manager-crds +export NAMESPACE=cozy-cert-manager + include ../../../hack/package.mk -update: - rm -rf charts - helm repo add jetstack https://charts.jetstack.io - helm repo update jetstack - helm pull jetstack/cert-manager --untar --untardir charts - rm -f -- `find charts/cert-manager/templates -maxdepth 1 -mindepth 1 | grep -v 'crds.yaml\|_helpers.tpl'` diff --git a/packages/system/cert-manager-crds/charts/cert-manager/Chart.yaml b/packages/system/cert-manager-crds/charts/cert-manager/Chart.yaml deleted file mode 100644 index 300db669..00000000 --- a/packages/system/cert-manager-crds/charts/cert-manager/Chart.yaml +++ /dev/null @@ -1,26 +0,0 @@ -annotations: - artifacthub.io/category: security - artifacthub.io/license: Apache-2.0 - artifacthub.io/prerelease: "false" - artifacthub.io/signKey: | - fingerprint: 1020CF3C033D4F35BAE1C19E1226061C665DF13E - url: https://cert-manager.io/public-keys/cert-manager-keyring-2021-09-20-1020CF3C033D4F35BAE1C19E1226061C665DF13E.gpg -apiVersion: v2 -appVersion: v1.16.3 -description: A Helm chart for cert-manager -home: https://cert-manager.io -icon: https://raw.githubusercontent.com/cert-manager/community/4d35a69437d21b76322157e6284be4cd64e6d2b7/logo/logo-small.png -keywords: -- cert-manager -- kube-lego -- letsencrypt -- tls -kubeVersion: '>= 1.22.0-0' -maintainers: -- email: cert-manager-maintainers@googlegroups.com - name: cert-manager-maintainers - url: https://cert-manager.io -name: cert-manager -sources: -- https://github.com/cert-manager/cert-manager -version: v1.16.3 diff --git a/packages/system/cert-manager-crds/charts/cert-manager/README.md b/packages/system/cert-manager-crds/charts/cert-manager/README.md deleted file mode 100644 index 6fa25cc9..00000000 --- a/packages/system/cert-manager-crds/charts/cert-manager/README.md +++ /dev/null @@ -1,1994 +0,0 @@ -# cert-manager - -cert-manager is a Kubernetes addon to automate the management and issuance of -TLS certificates from various issuing sources. - -It will ensure certificates are valid and up to date periodically, and attempt -to renew certificates at an appropriate time before expiry. - -## Prerequisites - -- Kubernetes 1.22+ - -## Installing the Chart - -Full installation instructions, including details on how to configure extra -functionality in cert-manager can be found in the [installation docs](https://cert-manager.io/docs/installation/kubernetes/). - -Before installing the chart, you must first install the cert-manager CustomResourceDefinition resources. -This is performed in a separate step to allow you to easily uninstall and reinstall cert-manager without deleting your installed custom resources. - -```bash -$ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.3/cert-manager.crds.yaml -``` - -To install the chart with the release name `cert-manager`: - -```console -## Add the Jetstack Helm repository -$ helm repo add jetstack https://charts.jetstack.io --force-update - -## Install the cert-manager helm chart -$ helm install cert-manager --namespace cert-manager --version v1.16.3 jetstack/cert-manager -``` - -In order to begin issuing certificates, you will need to set up a ClusterIssuer -or Issuer resource (for example, by creating a 'letsencrypt-staging' issuer). - -More information on the different types of issuers and how to configure them -can be found in [our documentation](https://cert-manager.io/docs/configuration/). - -For information on how to configure cert-manager to automatically provision -Certificates for Ingress resources, take a look at the -[Securing Ingresses documentation](https://cert-manager.io/docs/usage/ingress/). - -> **Tip**: List all releases using `helm list` - -## Upgrading the Chart - -Special considerations may be required when upgrading the Helm chart, and these -are documented in our full [upgrading guide](https://cert-manager.io/docs/installation/upgrading/). - -**Please check here before performing upgrades!** - -## Uninstalling the Chart - -To uninstall/delete the `cert-manager` deployment: - -```console -$ helm delete cert-manager --namespace cert-manager -``` - -The command removes all the Kubernetes components associated with the chart and deletes the release. - -If you want to completely uninstall cert-manager from your cluster, you will also need to -delete the previously installed CustomResourceDefinition resources: - -```console -$ kubectl delete -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.3/cert-manager.crds.yaml -``` - -## Configuration - - -### Global - -#### **global.imagePullSecrets** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -Reference to one or more secrets to be used when pulling images. For more information, see [Pull an Image from a Private Registry](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/). - -For example: - -```yaml -imagePullSecrets: - - name: "image-pull-secret" -``` -#### **global.commonLabels** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -Labels to apply to all resources. -Please note that this does not add labels to the resources created dynamically by the controllers. For these resources, you have to add the labels in the template in the cert-manager custom resource: For example, podTemplate/ ingressTemplate in ACMEChallengeSolverHTTP01Ingress. For more information, see the [cert-manager documentation](https://cert-manager.io/docs/reference/api-docs/#acme.cert-manager.io/v1.ACMEChallengeSolverHTTP01Ingress). -For example, secretTemplate in CertificateSpec -For more information, see the [cert-manager documentation](https://cert-manager.io/docs/reference/api-docs/#cert-manager.io/v1.CertificateSpec). -#### **global.revisionHistoryLimit** ~ `number` - -The number of old ReplicaSets to retain to allow rollback (if not set, the default Kubernetes value is set to 10). - -#### **global.priorityClassName** ~ `string` -> Default value: -> ```yaml -> "" -> ``` - -The optional priority class to be used for the cert-manager pods. -#### **global.rbac.create** ~ `bool` -> Default value: -> ```yaml -> true -> ``` - -Create required ClusterRoles and ClusterRoleBindings for cert-manager. -#### **global.rbac.aggregateClusterRoles** ~ `bool` -> Default value: -> ```yaml -> true -> ``` - -Aggregate ClusterRoles to Kubernetes default user-facing roles. For more information, see [User-facing roles](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles) -#### **global.podSecurityPolicy.enabled** ~ `bool` -> Default value: -> ```yaml -> false -> ``` - -Create PodSecurityPolicy for cert-manager. - -Note that PodSecurityPolicy was deprecated in Kubernetes 1.21 and removed in Kubernetes 1.25. -#### **global.podSecurityPolicy.useAppArmor** ~ `bool` -> Default value: -> ```yaml -> true -> ``` - -Configure the PodSecurityPolicy to use AppArmor. -#### **global.logLevel** ~ `number` -> Default value: -> ```yaml -> 2 -> ``` - -Set the verbosity of cert-manager. A range of 0 - 6, with 6 being the most verbose. -#### **global.leaderElection.namespace** ~ `string` -> Default value: -> ```yaml -> kube-system -> ``` - -Override the namespace used for the leader election lease. -#### **global.leaderElection.leaseDuration** ~ `string` - -The duration that non-leader candidates will wait after observing a leadership renewal until attempting to acquire leadership of a led but unrenewed leader slot. This is effectively the maximum duration that a leader can be stopped before it is replaced by another candidate. - -#### **global.leaderElection.renewDeadline** ~ `string` - -The interval between attempts by the acting master to renew a leadership slot before it stops leading. This must be less than or equal to the lease duration. - -#### **global.leaderElection.retryPeriod** ~ `string` - -The duration the clients should wait between attempting acquisition and renewal of a leadership. - -#### **installCRDs** ~ `bool` -> Default value: -> ```yaml -> false -> ``` - -This option is equivalent to setting crds.enabled=true and crds.keep=true. Deprecated: use crds.enabled and crds.keep instead. -#### **crds.enabled** ~ `bool` -> Default value: -> ```yaml -> false -> ``` - -This option decides if the CRDs should be installed as part of the Helm installation. -#### **crds.keep** ~ `bool` -> Default value: -> ```yaml -> true -> ``` - -This option makes it so that the "helm.sh/resource-policy": keep annotation is added to the CRD. This will prevent Helm from uninstalling the CRD when the Helm release is uninstalled. WARNING: when the CRDs are removed, all cert-manager custom resources -(Certificates, Issuers, ...) will be removed too by the garbage collector. -### Controller - -#### **replicaCount** ~ `number` -> Default value: -> ```yaml -> 1 -> ``` - -The number of replicas of the cert-manager controller to run. - -The default is 1, but in production set this to 2 or 3 to provide high availability. - -If `replicas > 1`, consider setting `podDisruptionBudget.enabled=true`. - -Note that cert-manager uses leader election to ensure that there can only be a single instance active at a time. -#### **strategy** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -Deployment update strategy for the cert-manager controller deployment. For more information, see the [Kubernetes documentation](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy). - -For example: - -```yaml -strategy: - type: RollingUpdate - rollingUpdate: - maxSurge: 0 - maxUnavailable: 1 -``` -#### **podDisruptionBudget.enabled** ~ `bool` -> Default value: -> ```yaml -> false -> ``` - -Enable or disable the PodDisruptionBudget resource. - -This prevents downtime during voluntary disruptions such as during a Node upgrade. For example, the PodDisruptionBudget will block `kubectl drain` if it is used on the Node where the only remaining cert-manager -Pod is currently running. -#### **podDisruptionBudget.minAvailable** ~ `unknown` - -This configures the minimum available pods for disruptions. It can either be set to an integer (e.g. 1) or a percentage value (e.g. 25%). -It cannot be used if `maxUnavailable` is set. - - -#### **podDisruptionBudget.maxUnavailable** ~ `unknown` - -This configures the maximum unavailable pods for disruptions. It can either be set to an integer (e.g. 1) or a percentage value (e.g. 25%). it cannot be used if `minAvailable` is set. - - -#### **featureGates** ~ `string` -> Default value: -> ```yaml -> "" -> ``` - -A comma-separated list of feature gates that should be enabled on the controller pod. -#### **maxConcurrentChallenges** ~ `number` -> Default value: -> ```yaml -> 60 -> ``` - -The maximum number of challenges that can be scheduled as 'processing' at once. -#### **image.registry** ~ `string` - -The container registry to pull the manager image from. - -#### **image.repository** ~ `string` -> Default value: -> ```yaml -> quay.io/jetstack/cert-manager-controller -> ``` - -The container image for the cert-manager controller. - -#### **image.tag** ~ `string` - -Override the image tag to deploy by setting this variable. If no value is set, the chart's appVersion is used. - -#### **image.digest** ~ `string` - -Setting a digest will override any tag. - -#### **image.pullPolicy** ~ `string` -> Default value: -> ```yaml -> IfNotPresent -> ``` - -Kubernetes imagePullPolicy on Deployment. -#### **clusterResourceNamespace** ~ `string` -> Default value: -> ```yaml -> "" -> ``` - -Override the namespace used to store DNS provider credentials etc. for ClusterIssuer resources. By default, the same namespace as cert-manager is deployed within is used. This namespace will not be automatically created by the Helm chart. -#### **namespace** ~ `string` -> Default value: -> ```yaml -> "" -> ``` - -This namespace allows you to define where the services are installed into. If not set then they use the namespace of the release. This is helpful when installing cert manager as a chart dependency (sub chart). -#### **fullnameOverride** ~ `string` - -Override the "cert-manager.fullname" value. This value is used as part of most of the names of the resources created by this Helm chart. - -#### **nameOverride** ~ `string` - -Override the "cert-manager.name" value, which is used to annotate some of the resources that are created by this Chart (using "app.kubernetes.io/name"). NOTE: There are some inconsistencies in the Helm chart when it comes to these annotations (some resources use eg. "cainjector.name" which resolves to the value "cainjector"). - -#### **serviceAccount.create** ~ `bool` -> Default value: -> ```yaml -> true -> ``` - -Specifies whether a service account should be created. -#### **serviceAccount.name** ~ `string` - -The name of the service account to use. -If not set and create is true, a name is generated using the fullname template. - -#### **serviceAccount.annotations** ~ `object` - -Optional additional annotations to add to the controller's Service Account. - -#### **serviceAccount.labels** ~ `object` - -Optional additional labels to add to the controller's Service Account. - -#### **serviceAccount.automountServiceAccountToken** ~ `bool` -> Default value: -> ```yaml -> true -> ``` - -Automount API credentials for a Service Account. -#### **automountServiceAccountToken** ~ `bool` - -Automounting API credentials for a particular pod. - -#### **enableCertificateOwnerRef** ~ `bool` -> Default value: -> ```yaml -> false -> ``` - -When this flag is enabled, secrets will be automatically removed when the certificate resource is deleted. -#### **config** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -This property is used to configure options for the controller pod. This allows setting options that would usually be provided using flags. - -If `apiVersion` and `kind` are unspecified they default to the current latest version (currently `controller.config.cert-manager.io/v1alpha1`). You can pin the version by specifying the `apiVersion` yourself. - -For example: - -```yaml -config: - apiVersion: controller.config.cert-manager.io/v1alpha1 - kind: ControllerConfiguration - logging: - verbosity: 2 - format: text - leaderElectionConfig: - namespace: kube-system - kubernetesAPIQPS: 9000 - kubernetesAPIBurst: 9000 - numberOfConcurrentWorkers: 200 - featureGates: - AdditionalCertificateOutputFormats: true - DisallowInsecureCSRUsageDefinition: true - ExperimentalCertificateSigningRequestControllers: true - ExperimentalGatewayAPISupport: true - LiteralCertificateSubject: true - SecretsFilteredCaching: true - ServerSideApply: true - StableCertificateRequestName: true - UseCertificateRequestBasicConstraints: true - ValidateCAA: true - # Configure the metrics server for TLS - # See https://cert-manager.io/docs/devops-tips/prometheus-metrics/#tls - metricsTLSConfig: - dynamic: - secretNamespace: "cert-manager" - secretName: "cert-manager-metrics-ca" - dnsNames: - - cert-manager-metrics -``` -#### **dns01RecursiveNameservers** ~ `string` -> Default value: -> ```yaml -> "" -> ``` - -A comma-separated string with the host and port of the recursive nameservers cert-manager should query. -#### **dns01RecursiveNameserversOnly** ~ `bool` -> Default value: -> ```yaml -> false -> ``` - -Forces cert-manager to use only the recursive nameservers for verification. Enabling this option could cause the DNS01 self check to take longer owing to caching performed by the recursive nameservers. -#### **disableAutoApproval** ~ `bool` -> Default value: -> ```yaml -> false -> ``` - -Option to disable cert-manager's build-in auto-approver. The auto-approver approves all CertificateRequests that reference issuers matching the 'approveSignerNames' option. This 'disableAutoApproval' option is useful when you want to make all approval decisions using a different approver (like approver-policy - https://github.com/cert-manager/approver-policy). -#### **approveSignerNames** ~ `array` -> Default value: -> ```yaml -> - issuers.cert-manager.io/* -> - clusterissuers.cert-manager.io/* -> ``` - -List of signer names that cert-manager will approve by default. CertificateRequests referencing these signer names will be auto-approved by cert-manager. Defaults to just approving the cert-manager.io Issuer and ClusterIssuer issuers. When set to an empty array, ALL issuers will be auto-approved by cert-manager. To disable the auto-approval, because eg. you are using approver-policy, you can enable 'disableAutoApproval'. -ref: https://cert-manager.io/docs/concepts/certificaterequest/#approval - -#### **extraArgs** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -Additional command line flags to pass to cert-manager controller binary. To see all available flags run `docker run quay.io/jetstack/cert-manager-controller: --help`. - -Use this flag to enable or disable arbitrary controllers. For example, to disable the CertificateRequests approver. - -For example: - -```yaml -extraArgs: - - --controllers=*,-certificaterequests-approver -``` -#### **extraEnv** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -Additional environment variables to pass to cert-manager controller binary. -For example: - -```yaml -extraEnv: -- name: SOME_VAR - value: 'some value' -``` -#### **resources** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -Resources to provide to the cert-manager controller pod. - -For example: - -```yaml -requests: - cpu: 10m - memory: 32Mi -``` - -For more information, see [Resource Management for Pods and Containers](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/). -#### **securityContext** ~ `object` -> Default value: -> ```yaml -> runAsNonRoot: true -> seccompProfile: -> type: RuntimeDefault -> ``` - -Pod Security Context. -For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/). - -#### **containerSecurityContext** ~ `object` -> Default value: -> ```yaml -> allowPrivilegeEscalation: false -> capabilities: -> drop: -> - ALL -> readOnlyRootFilesystem: true -> ``` - -Container Security Context to be set on the controller component container. For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/). - -#### **volumes** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -Additional volumes to add to the cert-manager controller pod. -#### **volumeMounts** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -Additional volume mounts to add to the cert-manager controller container. -#### **deploymentAnnotations** ~ `object` - -Optional additional annotations to add to the controller Deployment. - -#### **podAnnotations** ~ `object` - -Optional additional annotations to add to the controller Pods. - -#### **podLabels** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -Optional additional labels to add to the controller Pods. -#### **serviceAnnotations** ~ `object` - -Optional annotations to add to the controller Service. - -#### **serviceLabels** ~ `object` - -Optional additional labels to add to the controller Service. - -#### **serviceIPFamilyPolicy** ~ `string` - -Optionally set the IP family policy for the controller Service to configure dual-stack; see [Configure dual-stack](https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services). - -#### **serviceIPFamilies** ~ `array` - -Optionally set the IP families for the controller Service that should be supported, in the order in which they should be applied to ClusterIP. Can be IPv4 and/or IPv6. - -#### **podDnsPolicy** ~ `string` - -Pod DNS policy. -For more information, see [Pod's DNS Policy](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy). - -#### **podDnsConfig** ~ `object` - -Pod DNS configuration. The podDnsConfig field is optional and can work with any podDnsPolicy settings. However, when a Pod's dnsPolicy is set to "None", the dnsConfig field has to be specified. For more information, see [Pod's DNS Config](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-dns-config). - -#### **hostAliases** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -Optional hostAliases for cert-manager-controller pods. May be useful when performing ACME DNS-01 self checks. -#### **nodeSelector** ~ `object` -> Default value: -> ```yaml -> kubernetes.io/os: linux -> ``` - -The nodeSelector on Pods tells Kubernetes to schedule Pods on the nodes with matching labels. For more information, see [Assigning Pods to Nodes](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). - -This default ensures that Pods are only scheduled to Linux nodes. It prevents Pods being scheduled to Windows nodes in a mixed OS cluster. - -#### **ingressShim.defaultIssuerName** ~ `string` - -Optional default issuer to use for ingress resources. - -#### **ingressShim.defaultIssuerKind** ~ `string` - -Optional default issuer kind to use for ingress resources. - -#### **ingressShim.defaultIssuerGroup** ~ `string` - -Optional default issuer group to use for ingress resources. - -#### **http_proxy** ~ `string` - -Configures the HTTP_PROXY environment variable where a HTTP proxy is required. - -#### **https_proxy** ~ `string` - -Configures the HTTPS_PROXY environment variable where a HTTP proxy is required. - -#### **no_proxy** ~ `string` - -Configures the NO_PROXY environment variable where a HTTP proxy is required, but certain domains should be excluded. - -#### **affinity** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -A Kubernetes Affinity, if required. For more information, see [Affinity v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#affinity-v1-core). - -For example: - -```yaml -affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: foo.bar.com/role - operator: In - values: - - master -``` -#### **tolerations** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -A list of Kubernetes Tolerations, if required. For more information, see [Toleration v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#toleration-v1-core). - -For example: - -```yaml -tolerations: -- key: foo.bar.com/role - operator: Equal - value: master - effect: NoSchedule -``` -#### **topologySpreadConstraints** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -A list of Kubernetes TopologySpreadConstraints, if required. For more information, see [Topology spread constraint v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#topologyspreadconstraint-v1-core - -For example: - -```yaml -topologySpreadConstraints: -- maxSkew: 2 - topologyKey: topology.kubernetes.io/zone - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app.kubernetes.io/instance: cert-manager - app.kubernetes.io/component: controller -``` -#### **livenessProbe** ~ `object` -> Default value: -> ```yaml -> enabled: true -> failureThreshold: 8 -> initialDelaySeconds: 10 -> periodSeconds: 10 -> successThreshold: 1 -> timeoutSeconds: 15 -> ``` - -LivenessProbe settings for the controller container of the controller Pod. - -This is enabled by default, in order to enable the clock-skew liveness probe that restarts the controller in case of a skew between the system clock and the monotonic clock. LivenessProbe durations and thresholds are based on those used for the Kubernetes controller-manager. For more information see the following on the -[Kubernetes GitHub repository](https://github.com/kubernetes/kubernetes/blob/806b30170c61a38fedd54cc9ede4cd6275a1ad3b/cmd/kubeadm/app/util/staticpod/utils.go#L241-L245) - -#### **enableServiceLinks** ~ `bool` -> Default value: -> ```yaml -> false -> ``` - -enableServiceLinks indicates whether information about services should be injected into the pod's environment variables, matching the syntax of Docker links. -### Prometheus - -#### **prometheus.enabled** ~ `bool` -> Default value: -> ```yaml -> true -> ``` - -Enable Prometheus monitoring for the cert-manager controller and webhook. If you use the Prometheus Operator, set prometheus.podmonitor.enabled or prometheus.servicemonitor.enabled, to create a PodMonitor or a -ServiceMonitor resource. -Otherwise, 'prometheus.io' annotations are added to the cert-manager and cert-manager-webhook Deployments. Note that you can not enable both PodMonitor and ServiceMonitor as they are mutually exclusive. Enabling both will result in an error. -#### **prometheus.servicemonitor.enabled** ~ `bool` -> Default value: -> ```yaml -> false -> ``` - -Create a ServiceMonitor to add cert-manager to Prometheus. -#### **prometheus.servicemonitor.namespace** ~ `string` - -The namespace that the service monitor should live in, defaults to the cert-manager namespace. - -#### **prometheus.servicemonitor.prometheusInstance** ~ `string` -> Default value: -> ```yaml -> default -> ``` - -Specifies the `prometheus` label on the created ServiceMonitor. This is used when different Prometheus instances have label selectors matching different ServiceMonitors. -#### **prometheus.servicemonitor.targetPort** ~ `number` -> Default value: -> ```yaml -> 9402 -> ``` - -The target port to set on the ServiceMonitor. This must match the port that the cert-manager controller is listening on for metrics. -#### **prometheus.servicemonitor.path** ~ `string` -> Default value: -> ```yaml -> /metrics -> ``` - -The path to scrape for metrics. -#### **prometheus.servicemonitor.interval** ~ `string` -> Default value: -> ```yaml -> 60s -> ``` - -The interval to scrape metrics. -#### **prometheus.servicemonitor.scrapeTimeout** ~ `string` -> Default value: -> ```yaml -> 30s -> ``` - -The timeout before a metrics scrape fails. -#### **prometheus.servicemonitor.labels** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -Additional labels to add to the ServiceMonitor. -#### **prometheus.servicemonitor.annotations** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -Additional annotations to add to the ServiceMonitor. -#### **prometheus.servicemonitor.honorLabels** ~ `bool` -> Default value: -> ```yaml -> false -> ``` - -Keep labels from scraped data, overriding server-side labels. -#### **prometheus.servicemonitor.endpointAdditionalProperties** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -EndpointAdditionalProperties allows setting additional properties on the endpoint such as relabelings, metricRelabelings etc. - -For example: - -```yaml -endpointAdditionalProperties: - relabelings: - - action: replace - sourceLabels: - - __meta_kubernetes_pod_node_name - targetLabel: instance -``` - - - -#### **prometheus.podmonitor.enabled** ~ `bool` -> Default value: -> ```yaml -> false -> ``` - -Create a PodMonitor to add cert-manager to Prometheus. -#### **prometheus.podmonitor.namespace** ~ `string` - -The namespace that the pod monitor should live in, defaults to the cert-manager namespace. - -#### **prometheus.podmonitor.prometheusInstance** ~ `string` -> Default value: -> ```yaml -> default -> ``` - -Specifies the `prometheus` label on the created PodMonitor. This is used when different Prometheus instances have label selectors matching different PodMonitors. -#### **prometheus.podmonitor.path** ~ `string` -> Default value: -> ```yaml -> /metrics -> ``` - -The path to scrape for metrics. -#### **prometheus.podmonitor.interval** ~ `string` -> Default value: -> ```yaml -> 60s -> ``` - -The interval to scrape metrics. -#### **prometheus.podmonitor.scrapeTimeout** ~ `string` -> Default value: -> ```yaml -> 30s -> ``` - -The timeout before a metrics scrape fails. -#### **prometheus.podmonitor.labels** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -Additional labels to add to the PodMonitor. -#### **prometheus.podmonitor.annotations** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -Additional annotations to add to the PodMonitor. -#### **prometheus.podmonitor.honorLabels** ~ `bool` -> Default value: -> ```yaml -> false -> ``` - -Keep labels from scraped data, overriding server-side labels. -#### **prometheus.podmonitor.endpointAdditionalProperties** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -EndpointAdditionalProperties allows setting additional properties on the endpoint such as relabelings, metricRelabelings etc. - -For example: - -```yaml -endpointAdditionalProperties: - relabelings: - - action: replace - sourceLabels: - - __meta_kubernetes_pod_node_name - targetLabel: instance - # Configure the PodMonitor for TLS connections - # See https://cert-manager.io/docs/devops-tips/prometheus-metrics/#tls - scheme: https - tlsConfig: - serverName: cert-manager-metrics - ca: - secret: - name: cert-manager-metrics-ca - key: "tls.crt" -``` - - - -### Webhook - -#### **webhook.replicaCount** ~ `number` -> Default value: -> ```yaml -> 1 -> ``` - -Number of replicas of the cert-manager webhook to run. - -The default is 1, but in production set this to 2 or 3 to provide high availability. - -If `replicas > 1`, consider setting `webhook.podDisruptionBudget.enabled=true`. -#### **webhook.timeoutSeconds** ~ `number` -> Default value: -> ```yaml -> 30 -> ``` - -The number of seconds the API server should wait for the webhook to respond before treating the call as a failure. The value must be between 1 and 30 seconds. For more information, see -[Validating webhook configuration v1](https://kubernetes.io/docs/reference/kubernetes-api/extend-resources/validating-webhook-configuration-v1/). - -The default is set to the maximum value of 30 seconds as users sometimes report that the connection between the K8S API server and the cert-manager webhook server times out. If *this* timeout is reached, the error message will be "context deadline exceeded", which doesn't help the user diagnose what phase of the HTTPS connection timed out. For example, it could be during DNS resolution, TCP connection, TLS negotiation, HTTP negotiation, or slow HTTP response from the webhook server. By setting this timeout to its maximum value the underlying timeout error message has more chance of being returned to the end user. -#### **webhook.config** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -This is used to configure options for the webhook pod. This allows setting options that would usually be provided using flags. - -If `apiVersion` and `kind` are unspecified they default to the current latest version (currently `webhook.config.cert-manager.io/v1alpha1`). You can pin the version by specifying the `apiVersion` yourself. - -For example: - -```yaml -apiVersion: webhook.config.cert-manager.io/v1alpha1 -kind: WebhookConfiguration -# The port that the webhook listens on for requests. -# In GKE private clusters, by default Kubernetes apiservers are allowed to -# talk to the cluster nodes only on 443 and 10250. Configuring -# securePort: 10250 therefore will work out-of-the-box without needing to add firewall -# rules or requiring NET_BIND_SERVICE capabilities to bind port numbers < 1000. -# This should be uncommented and set as a default by the chart once -# the apiVersion of WebhookConfiguration graduates beyond v1alpha1. -securePort: 10250 -# Configure the metrics server for TLS -# See https://cert-manager.io/docs/devops-tips/prometheus-metrics/#tls -metricsTLSConfig: - dynamic: - secretNamespace: "cert-manager" - secretName: "cert-manager-metrics-ca" - dnsNames: - - cert-manager-metrics -``` -#### **webhook.strategy** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -The update strategy for the cert-manager webhook deployment. For more information, see the [Kubernetes documentation](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy) - -For example: - -```yaml -strategy: - type: RollingUpdate - rollingUpdate: - maxSurge: 0 - maxUnavailable: 1 -``` -#### **webhook.securityContext** ~ `object` -> Default value: -> ```yaml -> runAsNonRoot: true -> seccompProfile: -> type: RuntimeDefault -> ``` - -Pod Security Context to be set on the webhook component Pod. For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/). - -#### **webhook.containerSecurityContext** ~ `object` -> Default value: -> ```yaml -> allowPrivilegeEscalation: false -> capabilities: -> drop: -> - ALL -> readOnlyRootFilesystem: true -> ``` - -Container Security Context to be set on the webhook component container. For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/). - -#### **webhook.podDisruptionBudget.enabled** ~ `bool` -> Default value: -> ```yaml -> false -> ``` - -Enable or disable the PodDisruptionBudget resource. - -This prevents downtime during voluntary disruptions such as during a Node upgrade. For example, the PodDisruptionBudget will block `kubectl drain` if it is used on the Node where the only remaining cert-manager -Pod is currently running. -#### **webhook.podDisruptionBudget.minAvailable** ~ `unknown` - -This property configures the minimum available pods for disruptions. Can either be set to an integer (e.g. 1) or a percentage value (e.g. 25%). -It cannot be used if `maxUnavailable` is set. - - -#### **webhook.podDisruptionBudget.maxUnavailable** ~ `unknown` - -This property configures the maximum unavailable pods for disruptions. Can either be set to an integer (e.g. 1) or a percentage value (e.g. 25%). -It cannot be used if `minAvailable` is set. - - -#### **webhook.deploymentAnnotations** ~ `object` - -Optional additional annotations to add to the webhook Deployment. - -#### **webhook.podAnnotations** ~ `object` - -Optional additional annotations to add to the webhook Pods. - -#### **webhook.serviceAnnotations** ~ `object` - -Optional additional annotations to add to the webhook Service. - -#### **webhook.mutatingWebhookConfigurationAnnotations** ~ `object` - -Optional additional annotations to add to the webhook MutatingWebhookConfiguration. - -#### **webhook.validatingWebhookConfigurationAnnotations** ~ `object` - -Optional additional annotations to add to the webhook ValidatingWebhookConfiguration. - -#### **webhook.validatingWebhookConfiguration.namespaceSelector** ~ `object` -> Default value: -> ```yaml -> matchExpressions: -> - key: cert-manager.io/disable-validation -> operator: NotIn -> values: -> - "true" -> ``` - -Configure spec.namespaceSelector for validating webhooks. - -#### **webhook.mutatingWebhookConfiguration.namespaceSelector** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -Configure spec.namespaceSelector for mutating webhooks. - -#### **webhook.extraArgs** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -Additional command line flags to pass to cert-manager webhook binary. To see all available flags run `docker run quay.io/jetstack/cert-manager-webhook: --help`. -#### **webhook.extraEnv** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -Additional environment variables to pass to cert-manager webhook binary. -For example: - -```yaml -extraEnv: -- name: SOME_VAR - value: 'some value' -``` -#### **webhook.featureGates** ~ `string` -> Default value: -> ```yaml -> "" -> ``` - -Comma separated list of feature gates that should be enabled on the webhook pod. -#### **webhook.resources** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -Resources to provide to the cert-manager webhook pod. - -For example: - -```yaml -requests: - cpu: 10m - memory: 32Mi -``` - -For more information, see [Resource Management for Pods and Containers](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/). -#### **webhook.livenessProbe** ~ `object` -> Default value: -> ```yaml -> failureThreshold: 3 -> initialDelaySeconds: 60 -> periodSeconds: 10 -> successThreshold: 1 -> timeoutSeconds: 1 -> ``` - -Liveness probe values. -For more information, see [Container probes](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes). - -#### **webhook.readinessProbe** ~ `object` -> Default value: -> ```yaml -> failureThreshold: 3 -> initialDelaySeconds: 5 -> periodSeconds: 5 -> successThreshold: 1 -> timeoutSeconds: 1 -> ``` - -Readiness probe values. -For more information, see [Container probes](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes). - -#### **webhook.nodeSelector** ~ `object` -> Default value: -> ```yaml -> kubernetes.io/os: linux -> ``` - -The nodeSelector on Pods tells Kubernetes to schedule Pods on the nodes with matching labels. For more information, see [Assigning Pods to Nodes](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). - -This default ensures that Pods are only scheduled to Linux nodes. It prevents Pods being scheduled to Windows nodes in a mixed OS cluster. - -#### **webhook.affinity** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -A Kubernetes Affinity, if required. For more information, see [Affinity v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#affinity-v1-core). - -For example: - -```yaml -affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: foo.bar.com/role - operator: In - values: - - master -``` -#### **webhook.tolerations** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -A list of Kubernetes Tolerations, if required. For more information, see [Toleration v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#toleration-v1-core). - -For example: - -```yaml -tolerations: -- key: foo.bar.com/role - operator: Equal - value: master - effect: NoSchedule -``` -#### **webhook.topologySpreadConstraints** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -A list of Kubernetes TopologySpreadConstraints, if required. For more information, see [Topology spread constraint v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#topologyspreadconstraint-v1-core). - -For example: - -```yaml -topologySpreadConstraints: -- maxSkew: 2 - topologyKey: topology.kubernetes.io/zone - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app.kubernetes.io/instance: cert-manager - app.kubernetes.io/component: controller -``` -#### **webhook.podLabels** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -Optional additional labels to add to the Webhook Pods. -#### **webhook.serviceLabels** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -Optional additional labels to add to the Webhook Service. -#### **webhook.serviceIPFamilyPolicy** ~ `string` -> Default value: -> ```yaml -> "" -> ``` - -Optionally set the IP family policy for the controller Service to configure dual-stack; see [Configure dual-stack](https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services). -#### **webhook.serviceIPFamilies** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -Optionally set the IP families for the controller Service that should be supported, in the order in which they should be applied to ClusterIP. Can be IPv4 and/or IPv6. -#### **webhook.image.registry** ~ `string` - -The container registry to pull the webhook image from. - -#### **webhook.image.repository** ~ `string` -> Default value: -> ```yaml -> quay.io/jetstack/cert-manager-webhook -> ``` - -The container image for the cert-manager webhook - -#### **webhook.image.tag** ~ `string` - -Override the image tag to deploy by setting this variable. If no value is set, the chart's appVersion will be used. - -#### **webhook.image.digest** ~ `string` - -Setting a digest will override any tag - -#### **webhook.image.pullPolicy** ~ `string` -> Default value: -> ```yaml -> IfNotPresent -> ``` - -Kubernetes imagePullPolicy on Deployment. -#### **webhook.serviceAccount.create** ~ `bool` -> Default value: -> ```yaml -> true -> ``` - -Specifies whether a service account should be created. -#### **webhook.serviceAccount.name** ~ `string` - -The name of the service account to use. -If not set and create is true, a name is generated using the fullname template. - -#### **webhook.serviceAccount.annotations** ~ `object` - -Optional additional annotations to add to the webhook's Service Account. - -#### **webhook.serviceAccount.labels** ~ `object` - -Optional additional labels to add to the webhook's Service Account. - -#### **webhook.serviceAccount.automountServiceAccountToken** ~ `bool` -> Default value: -> ```yaml -> true -> ``` - -Automount API credentials for a Service Account. -#### **webhook.automountServiceAccountToken** ~ `bool` - -Automounting API credentials for a particular pod. - -#### **webhook.securePort** ~ `number` -> Default value: -> ```yaml -> 10250 -> ``` - -The port that the webhook listens on for requests. In GKE private clusters, by default Kubernetes apiservers are allowed to talk to the cluster nodes only on 443 and 10250. Configuring securePort: 10250, therefore will work out-of-the-box without needing to add firewall rules or requiring NET_BIND_SERVICE capabilities to bind port numbers <1000. -#### **webhook.hostNetwork** ~ `bool` -> Default value: -> ```yaml -> false -> ``` - -Specifies if the webhook should be started in hostNetwork mode. - -Required for use in some managed kubernetes clusters (such as AWS EKS) with custom. CNI (such as calico), because control-plane managed by AWS cannot communicate with pods' IP CIDR and admission webhooks are not working - -Since the default port for the webhook conflicts with kubelet on the host network, `webhook.securePort` should be changed to an available port if running in hostNetwork mode. -#### **webhook.serviceType** ~ `string` -> Default value: -> ```yaml -> ClusterIP -> ``` - -Specifies how the service should be handled. Useful if you want to expose the webhook outside of the cluster. In some cases, the control plane cannot reach internal services. -#### **webhook.loadBalancerIP** ~ `string` - -Specify the load balancer IP for the created service. - -#### **webhook.url** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -Overrides the mutating webhook and validating webhook so they reach the webhook service using the `url` field instead of a service. -#### **webhook.networkPolicy.enabled** ~ `bool` -> Default value: -> ```yaml -> false -> ``` - -Create network policies for the webhooks. -#### **webhook.networkPolicy.ingress** ~ `array` -> Default value: -> ```yaml -> - from: -> - ipBlock: -> cidr: 0.0.0.0/0 -> ``` - -Ingress rule for the webhook network policy. By default, it allows all inbound traffic. - -#### **webhook.networkPolicy.egress** ~ `array` -> Default value: -> ```yaml -> - ports: -> - port: 80 -> protocol: TCP -> - port: 443 -> protocol: TCP -> - port: 53 -> protocol: TCP -> - port: 53 -> protocol: UDP -> - port: 6443 -> protocol: TCP -> to: -> - ipBlock: -> cidr: 0.0.0.0/0 -> ``` - -Egress rule for the webhook network policy. By default, it allows all outbound traffic to ports 80 and 443, as well as DNS ports. - -#### **webhook.volumes** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -Additional volumes to add to the cert-manager controller pod. -#### **webhook.volumeMounts** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -Additional volume mounts to add to the cert-manager controller container. -#### **webhook.enableServiceLinks** ~ `bool` -> Default value: -> ```yaml -> false -> ``` - -enableServiceLinks indicates whether information about services should be injected into the pod's environment variables, matching the syntax of Docker links. -### CA Injector - -#### **cainjector.enabled** ~ `bool` -> Default value: -> ```yaml -> true -> ``` - -Create the CA Injector deployment -#### **cainjector.replicaCount** ~ `number` -> Default value: -> ```yaml -> 1 -> ``` - -The number of replicas of the cert-manager cainjector to run. - -The default is 1, but in production set this to 2 or 3 to provide high availability. - -If `replicas > 1`, consider setting `cainjector.podDisruptionBudget.enabled=true`. - -Note that cert-manager uses leader election to ensure that there can only be a single instance active at a time. -#### **cainjector.config** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -This is used to configure options for the cainjector pod. It allows setting options that are usually provided via flags. - -If `apiVersion` and `kind` are unspecified they default to the current latest version (currently `cainjector.config.cert-manager.io/v1alpha1`). You can pin the version by specifying the `apiVersion` yourself. - -For example: - -```yaml -apiVersion: cainjector.config.cert-manager.io/v1alpha1 -kind: CAInjectorConfiguration -logging: - verbosity: 2 - format: text -leaderElectionConfig: - namespace: kube-system -# Configure the metrics server for TLS -# See https://cert-manager.io/docs/devops-tips/prometheus-metrics/#tls -metricsTLSConfig: - dynamic: - secretNamespace: "cert-manager" - secretName: "cert-manager-metrics-ca" - dnsNames: - - cert-manager-metrics -``` -#### **cainjector.strategy** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -Deployment update strategy for the cert-manager cainjector deployment. For more information, see the [Kubernetes documentation](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy). - -For example: - -```yaml -strategy: - type: RollingUpdate - rollingUpdate: - maxSurge: 0 - maxUnavailable: 1 -``` -#### **cainjector.securityContext** ~ `object` -> Default value: -> ```yaml -> runAsNonRoot: true -> seccompProfile: -> type: RuntimeDefault -> ``` - -Pod Security Context to be set on the cainjector component Pod. For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/). - -#### **cainjector.containerSecurityContext** ~ `object` -> Default value: -> ```yaml -> allowPrivilegeEscalation: false -> capabilities: -> drop: -> - ALL -> readOnlyRootFilesystem: true -> ``` - -Container Security Context to be set on the cainjector component container. For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/). - -#### **cainjector.podDisruptionBudget.enabled** ~ `bool` -> Default value: -> ```yaml -> false -> ``` - -Enable or disable the PodDisruptionBudget resource. - -This prevents downtime during voluntary disruptions such as during a Node upgrade. For example, the PodDisruptionBudget will block `kubectl drain` if it is used on the Node where the only remaining cert-manager -Pod is currently running. -#### **cainjector.podDisruptionBudget.minAvailable** ~ `unknown` - -`minAvailable` configures the minimum available pods for disruptions. It can either be set to -an integer (e.g. 1) or a percentage value (e.g. 25%). -Cannot be used if `maxUnavailable` is set. - - -#### **cainjector.podDisruptionBudget.maxUnavailable** ~ `unknown` - -`maxUnavailable` configures the maximum unavailable pods for disruptions. It can either be set to -an integer (e.g. 1) or a percentage value (e.g. 25%). -Cannot be used if `minAvailable` is set. - - -#### **cainjector.deploymentAnnotations** ~ `object` - -Optional additional annotations to add to the cainjector Deployment. - -#### **cainjector.podAnnotations** ~ `object` - -Optional additional annotations to add to the cainjector Pods. - -#### **cainjector.serviceAnnotations** ~ `object` - -Optional additional annotations to add to the cainjector metrics Service. - -#### **cainjector.extraArgs** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -Additional command line flags to pass to cert-manager cainjector binary. To see all available flags run `docker run quay.io/jetstack/cert-manager-cainjector: --help`. -#### **cainjector.extraEnv** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -Additional environment variables to pass to cert-manager cainjector binary. -For example: - -```yaml -extraEnv: -- name: SOME_VAR - value: 'some value' -``` -#### **cainjector.featureGates** ~ `string` -> Default value: -> ```yaml -> "" -> ``` - -Comma separated list of feature gates that should be enabled on the cainjector pod. -#### **cainjector.resources** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -Resources to provide to the cert-manager cainjector pod. - -For example: - -```yaml -requests: - cpu: 10m - memory: 32Mi -``` - -For more information, see [Resource Management for Pods and Containers](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/). -#### **cainjector.nodeSelector** ~ `object` -> Default value: -> ```yaml -> kubernetes.io/os: linux -> ``` - -The nodeSelector on Pods tells Kubernetes to schedule Pods on the nodes with matching labels. For more information, see [Assigning Pods to Nodes](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). - -This default ensures that Pods are only scheduled to Linux nodes. It prevents Pods being scheduled to Windows nodes in a mixed OS cluster. - -#### **cainjector.affinity** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -A Kubernetes Affinity, if required. For more information, see [Affinity v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#affinity-v1-core). - -For example: - -```yaml -affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: foo.bar.com/role - operator: In - values: - - master -``` -#### **cainjector.tolerations** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -A list of Kubernetes Tolerations, if required. For more information, see [Toleration v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#toleration-v1-core). - -For example: - -```yaml -tolerations: -- key: foo.bar.com/role - operator: Equal - value: master - effect: NoSchedule -``` -#### **cainjector.topologySpreadConstraints** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -A list of Kubernetes TopologySpreadConstraints, if required. For more information, see [Topology spread constraint v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#topologyspreadconstraint-v1-core). - -For example: - -```yaml -topologySpreadConstraints: -- maxSkew: 2 - topologyKey: topology.kubernetes.io/zone - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app.kubernetes.io/instance: cert-manager - app.kubernetes.io/component: controller -``` -#### **cainjector.podLabels** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -Optional additional labels to add to the CA Injector Pods. -#### **cainjector.serviceLabels** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -Optional additional labels to add to the CA Injector metrics Service. -#### **cainjector.image.registry** ~ `string` - -The container registry to pull the cainjector image from. - -#### **cainjector.image.repository** ~ `string` -> Default value: -> ```yaml -> quay.io/jetstack/cert-manager-cainjector -> ``` - -The container image for the cert-manager cainjector - -#### **cainjector.image.tag** ~ `string` - -Override the image tag to deploy by setting this variable. If no value is set, the chart's appVersion will be used. - -#### **cainjector.image.digest** ~ `string` - -Setting a digest will override any tag. - -#### **cainjector.image.pullPolicy** ~ `string` -> Default value: -> ```yaml -> IfNotPresent -> ``` - -Kubernetes imagePullPolicy on Deployment. -#### **cainjector.serviceAccount.create** ~ `bool` -> Default value: -> ```yaml -> true -> ``` - -Specifies whether a service account should be created. -#### **cainjector.serviceAccount.name** ~ `string` - -The name of the service account to use. -If not set and create is true, a name is generated using the fullname template - -#### **cainjector.serviceAccount.annotations** ~ `object` - -Optional additional annotations to add to the cainjector's Service Account. - -#### **cainjector.serviceAccount.labels** ~ `object` - -Optional additional labels to add to the cainjector's Service Account. - -#### **cainjector.serviceAccount.automountServiceAccountToken** ~ `bool` -> Default value: -> ```yaml -> true -> ``` - -Automount API credentials for a Service Account. -#### **cainjector.automountServiceAccountToken** ~ `bool` - -Automounting API credentials for a particular pod. - -#### **cainjector.volumes** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -Additional volumes to add to the cert-manager controller pod. -#### **cainjector.volumeMounts** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -Additional volume mounts to add to the cert-manager controller container. -#### **cainjector.enableServiceLinks** ~ `bool` -> Default value: -> ```yaml -> false -> ``` - -enableServiceLinks indicates whether information about services should be injected into the pod's environment variables, matching the syntax of Docker links. -### ACME Solver - -#### **acmesolver.image.registry** ~ `string` - -The container registry to pull the acmesolver image from. - -#### **acmesolver.image.repository** ~ `string` -> Default value: -> ```yaml -> quay.io/jetstack/cert-manager-acmesolver -> ``` - -The container image for the cert-manager acmesolver. - -#### **acmesolver.image.tag** ~ `string` - -Override the image tag to deploy by setting this variable. If no value is set, the chart's appVersion is used. - -#### **acmesolver.image.digest** ~ `string` - -Setting a digest will override any tag. - -#### **acmesolver.image.pullPolicy** ~ `string` -> Default value: -> ```yaml -> IfNotPresent -> ``` - -Kubernetes imagePullPolicy on Deployment. -### Startup API Check - - -This startupapicheck is a Helm post-install hook that waits for the webhook endpoints to become available. The check is implemented using a Kubernetes Job - if you are injecting mesh sidecar proxies into cert-manager pods, ensure that they are not injected into this Job's pod. Otherwise, the installation may time out owing to the Job never being completed because the sidecar proxy does not exit. For more information, see [this note](https://github.com/cert-manager/cert-manager/pull/4414). -#### **startupapicheck.enabled** ~ `bool` -> Default value: -> ```yaml -> true -> ``` - -Enables the startup api check. -#### **startupapicheck.securityContext** ~ `object` -> Default value: -> ```yaml -> runAsNonRoot: true -> seccompProfile: -> type: RuntimeDefault -> ``` - -Pod Security Context to be set on the startupapicheck component Pod. For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/). - -#### **startupapicheck.containerSecurityContext** ~ `object` -> Default value: -> ```yaml -> allowPrivilegeEscalation: false -> capabilities: -> drop: -> - ALL -> readOnlyRootFilesystem: true -> ``` - -Container Security Context to be set on the controller component container. For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/). - -#### **startupapicheck.timeout** ~ `string` -> Default value: -> ```yaml -> 1m -> ``` - -Timeout for 'kubectl check api' command. -#### **startupapicheck.backoffLimit** ~ `number` -> Default value: -> ```yaml -> 4 -> ``` - -Job backoffLimit -#### **startupapicheck.jobAnnotations** ~ `object` -> Default value: -> ```yaml -> helm.sh/hook: post-install -> helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded -> helm.sh/hook-weight: "1" -> ``` - -Optional additional annotations to add to the startupapicheck Job. - -#### **startupapicheck.podAnnotations** ~ `object` - -Optional additional annotations to add to the startupapicheck Pods. - -#### **startupapicheck.extraArgs** ~ `array` -> Default value: -> ```yaml -> - -v -> ``` - -Additional command line flags to pass to startupapicheck binary. To see all available flags run `docker run quay.io/jetstack/cert-manager-startupapicheck: --help`. - -Verbose logging is enabled by default so that if startupapicheck fails, you can know what exactly caused the failure. Verbose logs include details of the webhook URL, IP address and TCP connect errors for example. - -#### **startupapicheck.extraEnv** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -Additional environment variables to pass to cert-manager startupapicheck binary. -For example: - -```yaml -extraEnv: -- name: SOME_VAR - value: 'some value' -``` -#### **startupapicheck.resources** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -Resources to provide to the cert-manager controller pod. - -For example: - -```yaml -requests: - cpu: 10m - memory: 32Mi -``` - -For more information, see [Resource Management for Pods and Containers](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/). -#### **startupapicheck.nodeSelector** ~ `object` -> Default value: -> ```yaml -> kubernetes.io/os: linux -> ``` - -The nodeSelector on Pods tells Kubernetes to schedule Pods on the nodes with matching labels. For more information, see [Assigning Pods to Nodes](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). - -This default ensures that Pods are only scheduled to Linux nodes. It prevents Pods being scheduled to Windows nodes in a mixed OS cluster. - -#### **startupapicheck.affinity** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -A Kubernetes Affinity, if required. For more information, see [Affinity v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#affinity-v1-core). -For example: - -```yaml -affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: foo.bar.com/role - operator: In - values: - - master -``` -#### **startupapicheck.tolerations** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -A list of Kubernetes Tolerations, if required. For more information, see [Toleration v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#toleration-v1-core). - -For example: - -```yaml -tolerations: -- key: foo.bar.com/role - operator: Equal - value: master - effect: NoSchedule -``` -#### **startupapicheck.podLabels** ~ `object` -> Default value: -> ```yaml -> {} -> ``` - -Optional additional labels to add to the startupapicheck Pods. -#### **startupapicheck.image.registry** ~ `string` - -The container registry to pull the startupapicheck image from. - -#### **startupapicheck.image.repository** ~ `string` -> Default value: -> ```yaml -> quay.io/jetstack/cert-manager-startupapicheck -> ``` - -The container image for the cert-manager startupapicheck. - -#### **startupapicheck.image.tag** ~ `string` - -Override the image tag to deploy by setting this variable. If no value is set, the chart's appVersion is used. - -#### **startupapicheck.image.digest** ~ `string` - -Setting a digest will override any tag. - -#### **startupapicheck.image.pullPolicy** ~ `string` -> Default value: -> ```yaml -> IfNotPresent -> ``` - -Kubernetes imagePullPolicy on Deployment. -#### **startupapicheck.rbac.annotations** ~ `object` -> Default value: -> ```yaml -> helm.sh/hook: post-install -> helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded -> helm.sh/hook-weight: "-5" -> ``` - -annotations for the startup API Check job RBAC and PSP resources. - -#### **startupapicheck.automountServiceAccountToken** ~ `bool` - -Automounting API credentials for a particular pod. - -#### **startupapicheck.serviceAccount.create** ~ `bool` -> Default value: -> ```yaml -> true -> ``` - -Specifies whether a service account should be created. -#### **startupapicheck.serviceAccount.name** ~ `string` - -The name of the service account to use. -If not set and create is true, a name is generated using the fullname template. - -#### **startupapicheck.serviceAccount.annotations** ~ `object` -> Default value: -> ```yaml -> helm.sh/hook: post-install -> helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded -> helm.sh/hook-weight: "-5" -> ``` - -Optional additional annotations to add to the Job's Service Account. - -#### **startupapicheck.serviceAccount.automountServiceAccountToken** ~ `bool` -> Default value: -> ```yaml -> true -> ``` - -Automount API credentials for a Service Account. - -#### **startupapicheck.serviceAccount.labels** ~ `object` - -Optional additional labels to add to the startupapicheck's Service Account. - -#### **startupapicheck.volumes** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -Additional volumes to add to the cert-manager controller pod. -#### **startupapicheck.volumeMounts** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -Additional volume mounts to add to the cert-manager controller container. -#### **startupapicheck.enableServiceLinks** ~ `bool` -> Default value: -> ```yaml -> false -> ``` - -enableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. -#### **extraObjects** ~ `array` -> Default value: -> ```yaml -> [] -> ``` - -Create dynamic manifests via values. - -For example: - -```yaml -extraObjects: - - | - apiVersion: v1 - kind: ConfigMap - metadata: - name: '{{ template "cert-manager.fullname" . }}-extra-configmap' -``` - - -### Default Security Contexts - -The default pod-level and container-level security contexts, below, adhere to the [restricted](https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted) Pod Security Standards policies. - -Default pod-level securityContext: -```yaml -runAsNonRoot: true -seccompProfile: - type: RuntimeDefault -``` - -Default containerSecurityContext: -```yaml -allowPrivilegeEscalation: false -capabilities: - drop: - - ALL -``` - -### Assigning Values - -Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. - -Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example, - -```console -$ helm install my-release -f values.yaml . -``` -> **Tip**: You can use the default [values.yaml](https://github.com/cert-manager/cert-manager/blob/master/deploy/charts/cert-manager/values.yaml) - -## Contributing - -This chart is maintained at [github.com/cert-manager/cert-manager](https://github.com/cert-manager/cert-manager/tree/master/deploy/charts/cert-manager). diff --git a/packages/system/cert-manager-crds/charts/cert-manager/templates/crds.yaml b/packages/system/cert-manager-crds/charts/cert-manager/templates/crds.yaml deleted file mode 100644 index 00930f9c..00000000 --- a/packages/system/cert-manager-crds/charts/cert-manager/templates/crds.yaml +++ /dev/null @@ -1,12013 +0,0 @@ -# {{- include "cert-manager.crd-check" . }} -# START crd {{- if or .Values.crds.enabled .Values.installCRDs }} -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: certificaterequests.cert-manager.io - # START annotations {{- if .Values.crds.keep }} - annotations: - helm.sh/resource-policy: keep - # END annotations {{- end }} - labels: - app: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/name: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/instance: '{{ .Release.Name }}' - # Generated labels {{- include "labels" . | nindent 4 }} -spec: - group: cert-manager.io - names: - kind: CertificateRequest - listKind: CertificateRequestList - plural: certificaterequests - shortNames: - - cr - - crs - singular: certificaterequest - categories: - - cert-manager - scope: Namespaced - versions: - - name: v1 - subresources: - status: {} - additionalPrinterColumns: - - jsonPath: .status.conditions[?(@.type=="Approved")].status - name: Approved - type: string - - jsonPath: .status.conditions[?(@.type=="Denied")].status - name: Denied - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .spec.issuerRef.name - name: Issuer - type: string - - jsonPath: .spec.username - name: Requester - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - priority: 1 - type: string - - jsonPath: .metadata.creationTimestamp - description: CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC. - name: Age - type: date - schema: - openAPIV3Schema: - description: |- - A CertificateRequest is used to request a signed certificate from one of the - configured issuers. - - All fields within the CertificateRequest's `spec` are immutable after creation. - A CertificateRequest will either succeed or fail, as denoted by its `Ready` status - condition and its `status.failureTime` field. - - A CertificateRequest is a one-shot resource, meaning it represents a single - point in time request for a certificate and cannot be re-used. - type: object - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: |- - Specification of the desired state of the CertificateRequest resource. - https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status - type: object - required: - - issuerRef - - request - properties: - duration: - description: |- - Requested 'duration' (i.e. lifetime) of the Certificate. Note that the - issuer may choose to ignore the requested duration, just like any other - requested attribute. - type: string - extra: - description: |- - Extra contains extra attributes of the user that created the CertificateRequest. - Populated by the cert-manager webhook on creation and immutable. - type: object - additionalProperties: - type: array - items: - type: string - groups: - description: |- - Groups contains group membership of the user that created the CertificateRequest. - Populated by the cert-manager webhook on creation and immutable. - type: array - items: - type: string - x-kubernetes-list-type: atomic - isCA: - description: |- - Requested basic constraints isCA value. Note that the issuer may choose - to ignore the requested isCA value, just like any other requested attribute. - - NOTE: If the CSR in the `Request` field has a BasicConstraints extension, - it must have the same isCA value as specified here. - - If true, this will automatically add the `cert sign` usage to the list - of requested `usages`. - type: boolean - issuerRef: - description: |- - Reference to the issuer responsible for issuing the certificate. - If the issuer is namespace-scoped, it must be in the same namespace - as the Certificate. If the issuer is cluster-scoped, it can be used - from any namespace. - - The `name` field of the reference must always be specified. - type: object - required: - - name - properties: - group: - description: Group of the resource being referred to. - type: string - kind: - description: Kind of the resource being referred to. - type: string - name: - description: Name of the resource being referred to. - type: string - request: - description: |- - The PEM-encoded X.509 certificate signing request to be submitted to the - issuer for signing. - - If the CSR has a BasicConstraints extension, its isCA attribute must - match the `isCA` value of this CertificateRequest. - If the CSR has a KeyUsage extension, its key usages must match the - key usages in the `usages` field of this CertificateRequest. - If the CSR has a ExtKeyUsage extension, its extended key usages - must match the extended key usages in the `usages` field of this - CertificateRequest. - type: string - format: byte - uid: - description: |- - UID contains the uid of the user that created the CertificateRequest. - Populated by the cert-manager webhook on creation and immutable. - type: string - usages: - description: |- - Requested key usages and extended key usages. - - NOTE: If the CSR in the `Request` field has uses the KeyUsage or - ExtKeyUsage extension, these extensions must have the same values - as specified here without any additional values. - - If unset, defaults to `digital signature` and `key encipherment`. - type: array - items: - description: |- - KeyUsage specifies valid usage contexts for keys. - See: - https://tools.ietf.org/html/rfc5280#section-4.2.1.3 - https://tools.ietf.org/html/rfc5280#section-4.2.1.12 - - Valid KeyUsage values are as follows: - "signing", - "digital signature", - "content commitment", - "key encipherment", - "key agreement", - "data encipherment", - "cert sign", - "crl sign", - "encipher only", - "decipher only", - "any", - "server auth", - "client auth", - "code signing", - "email protection", - "s/mime", - "ipsec end system", - "ipsec tunnel", - "ipsec user", - "timestamping", - "ocsp signing", - "microsoft sgc", - "netscape sgc" - type: string - enum: - - signing - - digital signature - - content commitment - - key encipherment - - key agreement - - data encipherment - - cert sign - - crl sign - - encipher only - - decipher only - - any - - server auth - - client auth - - code signing - - email protection - - s/mime - - ipsec end system - - ipsec tunnel - - ipsec user - - timestamping - - ocsp signing - - microsoft sgc - - netscape sgc - username: - description: |- - Username contains the name of the user that created the CertificateRequest. - Populated by the cert-manager webhook on creation and immutable. - type: string - status: - description: |- - Status of the CertificateRequest. - This is set and managed automatically. - Read-only. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status - type: object - properties: - ca: - description: |- - The PEM encoded X.509 certificate of the signer, also known as the CA - (Certificate Authority). - This is set on a best-effort basis by different issuers. - If not set, the CA is assumed to be unknown/not available. - type: string - format: byte - certificate: - description: |- - The PEM encoded X.509 certificate resulting from the certificate - signing request. - If not set, the CertificateRequest has either not been completed or has - failed. More information on failure can be found by checking the - `conditions` field. - type: string - format: byte - conditions: - description: |- - List of status conditions to indicate the status of a CertificateRequest. - Known condition types are `Ready`, `InvalidRequest`, `Approved` and `Denied`. - type: array - items: - description: CertificateRequestCondition contains condition information for a CertificateRequest. - type: object - required: - - status - - type - properties: - lastTransitionTime: - description: |- - LastTransitionTime is the timestamp corresponding to the last status - change of this condition. - type: string - format: date-time - message: - description: |- - Message is a human readable description of the details of the last - transition, complementing reason. - type: string - reason: - description: |- - Reason is a brief machine readable explanation for the condition's last - transition. - type: string - status: - description: Status of the condition, one of (`True`, `False`, `Unknown`). - type: string - enum: - - "True" - - "False" - - Unknown - type: - description: |- - Type of the condition, known values are (`Ready`, `InvalidRequest`, - `Approved`, `Denied`). - type: string - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - failureTime: - description: |- - FailureTime stores the time that this CertificateRequest failed. This is - used to influence garbage collection and back-off. - type: string - format: date-time - served: true - storage: true - -# END crd {{- end }} - ---- -# START crd {{- if or .Values.crds.enabled .Values.installCRDs }} -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: certificates.cert-manager.io - # START annotations {{- if .Values.crds.keep }} - annotations: - helm.sh/resource-policy: keep - # END annotations {{- end }} - labels: - app: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/name: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/instance: '{{ .Release.Name }}' - # Generated labels {{- include "labels" . | nindent 4 }} -spec: - group: cert-manager.io - names: - kind: Certificate - listKind: CertificateList - plural: certificates - shortNames: - - cert - - certs - singular: certificate - categories: - - cert-manager - scope: Namespaced - versions: - - name: v1 - subresources: - status: {} - additionalPrinterColumns: - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .spec.secretName - name: Secret - type: string - - jsonPath: .spec.issuerRef.name - name: Issuer - priority: 1 - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - priority: 1 - type: string - - jsonPath: .metadata.creationTimestamp - description: CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC. - name: Age - type: date - schema: - openAPIV3Schema: - description: |- - A Certificate resource should be created to ensure an up to date and signed - X.509 certificate is stored in the Kubernetes Secret resource named in `spec.secretName`. - - The stored certificate will be renewed before it expires (as configured by `spec.renewBefore`). - type: object - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: |- - Specification of the desired state of the Certificate resource. - https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status - type: object - required: - - issuerRef - - secretName - properties: - additionalOutputFormats: - description: |- - Defines extra output formats of the private key and signed certificate chain - to be written to this Certificate's target Secret. - - This is a Beta Feature enabled by default. It can be disabled with the - `--feature-gates=AdditionalCertificateOutputFormats=false` option set on both - the controller and webhook components. - type: array - items: - description: |- - CertificateAdditionalOutputFormat defines an additional output format of a - Certificate resource. These contain supplementary data formats of the signed - certificate chain and paired private key. - type: object - required: - - type - properties: - type: - description: |- - Type is the name of the format type that should be written to the - Certificate's target Secret. - type: string - enum: - - DER - - CombinedPEM - commonName: - description: |- - Requested common name X509 certificate subject attribute. - More info: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.6 - NOTE: TLS clients will ignore this value when any subject alternative name is - set (see https://tools.ietf.org/html/rfc6125#section-6.4.4). - - Should have a length of 64 characters or fewer to avoid generating invalid CSRs. - Cannot be set if the `literalSubject` field is set. - type: string - dnsNames: - description: Requested DNS subject alternative names. - type: array - items: - type: string - duration: - description: |- - Requested 'duration' (i.e. lifetime) of the Certificate. Note that the - issuer may choose to ignore the requested duration, just like any other - requested attribute. - - If unset, this defaults to 90 days. - Minimum accepted duration is 1 hour. - Value must be in units accepted by Go time.ParseDuration https://golang.org/pkg/time/#ParseDuration. - type: string - emailAddresses: - description: Requested email subject alternative names. - type: array - items: - type: string - encodeUsagesInRequest: - description: |- - Whether the KeyUsage and ExtKeyUsage extensions should be set in the encoded CSR. - - This option defaults to true, and should only be disabled if the target - issuer does not support CSRs with these X509 KeyUsage/ ExtKeyUsage extensions. - type: boolean - ipAddresses: - description: Requested IP address subject alternative names. - type: array - items: - type: string - isCA: - description: |- - Requested basic constraints isCA value. - The isCA value is used to set the `isCA` field on the created CertificateRequest - resources. Note that the issuer may choose to ignore the requested isCA value, just - like any other requested attribute. - - If true, this will automatically add the `cert sign` usage to the list - of requested `usages`. - type: boolean - issuerRef: - description: |- - Reference to the issuer responsible for issuing the certificate. - If the issuer is namespace-scoped, it must be in the same namespace - as the Certificate. If the issuer is cluster-scoped, it can be used - from any namespace. - - The `name` field of the reference must always be specified. - type: object - required: - - name - properties: - group: - description: Group of the resource being referred to. - type: string - kind: - description: Kind of the resource being referred to. - type: string - name: - description: Name of the resource being referred to. - type: string - keystores: - description: Additional keystore output formats to be stored in the Certificate's Secret. - type: object - properties: - jks: - description: |- - JKS configures options for storing a JKS keystore in the - `spec.secretName` Secret resource. - type: object - required: - - create - - passwordSecretRef - properties: - alias: - description: |- - Alias specifies the alias of the key in the keystore, required by the JKS format. - If not provided, the default alias `certificate` will be used. - type: string - create: - description: |- - Create enables JKS keystore creation for the Certificate. - If true, a file named `keystore.jks` will be created in the target - Secret resource, encrypted using the password stored in - `passwordSecretRef`. - The keystore file will be updated immediately. - If the issuer provided a CA certificate, a file named `truststore.jks` - will also be created in the target Secret resource, encrypted using the - password stored in `passwordSecretRef` - containing the issuing Certificate Authority - type: boolean - passwordSecretRef: - description: |- - PasswordSecretRef is a reference to a key in a Secret resource - containing the password used to encrypt the JKS keystore. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - pkcs12: - description: |- - PKCS12 configures options for storing a PKCS12 keystore in the - `spec.secretName` Secret resource. - type: object - required: - - create - - passwordSecretRef - properties: - create: - description: |- - Create enables PKCS12 keystore creation for the Certificate. - If true, a file named `keystore.p12` will be created in the target - Secret resource, encrypted using the password stored in - `passwordSecretRef`. - The keystore file will be updated immediately. - If the issuer provided a CA certificate, a file named `truststore.p12` will - also be created in the target Secret resource, encrypted using the - password stored in `passwordSecretRef` containing the issuing Certificate - Authority - type: boolean - passwordSecretRef: - description: |- - PasswordSecretRef is a reference to a key in a Secret resource - containing the password used to encrypt the PKCS12 keystore. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - profile: - description: |- - Profile specifies the key and certificate encryption algorithms and the HMAC algorithm - used to create the PKCS12 keystore. Default value is `LegacyRC2` for backward compatibility. - - If provided, allowed values are: - `LegacyRC2`: Deprecated. Not supported by default in OpenSSL 3 or Java 20. - `LegacyDES`: Less secure algorithm. Use this option for maximal compatibility. - `Modern2023`: Secure algorithm. Use this option in case you have to always use secure algorithms - (eg. because of company policy). Please note that the security of the algorithm is not that important - in reality, because the unencrypted certificate and private key are also stored in the Secret. - type: string - enum: - - LegacyRC2 - - LegacyDES - - Modern2023 - literalSubject: - description: |- - Requested X.509 certificate subject, represented using the LDAP "String - Representation of a Distinguished Name" [1]. - Important: the LDAP string format also specifies the order of the attributes - in the subject, this is important when issuing certs for LDAP authentication. - Example: `CN=foo,DC=corp,DC=example,DC=com` - More info [1]: https://datatracker.ietf.org/doc/html/rfc4514 - More info: https://github.com/cert-manager/cert-manager/issues/3203 - More info: https://github.com/cert-manager/cert-manager/issues/4424 - - Cannot be set if the `subject` or `commonName` field is set. - type: string - nameConstraints: - description: |- - x.509 certificate NameConstraint extension which MUST NOT be used in a non-CA certificate. - More Info: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.10 - - This is an Alpha Feature and is only enabled with the - `--feature-gates=NameConstraints=true` option set on both - the controller and webhook components. - type: object - properties: - critical: - description: if true then the name constraints are marked critical. - type: boolean - excluded: - description: |- - Excluded contains the constraints which must be disallowed. Any name matching a - restriction in the excluded field is invalid regardless - of information appearing in the permitted - type: object - properties: - dnsDomains: - description: DNSDomains is a list of DNS domains that are permitted or excluded. - type: array - items: - type: string - emailAddresses: - description: EmailAddresses is a list of Email Addresses that are permitted or excluded. - type: array - items: - type: string - ipRanges: - description: |- - IPRanges is a list of IP Ranges that are permitted or excluded. - This should be a valid CIDR notation. - type: array - items: - type: string - uriDomains: - description: URIDomains is a list of URI domains that are permitted or excluded. - type: array - items: - type: string - permitted: - description: Permitted contains the constraints in which the names must be located. - type: object - properties: - dnsDomains: - description: DNSDomains is a list of DNS domains that are permitted or excluded. - type: array - items: - type: string - emailAddresses: - description: EmailAddresses is a list of Email Addresses that are permitted or excluded. - type: array - items: - type: string - ipRanges: - description: |- - IPRanges is a list of IP Ranges that are permitted or excluded. - This should be a valid CIDR notation. - type: array - items: - type: string - uriDomains: - description: URIDomains is a list of URI domains that are permitted or excluded. - type: array - items: - type: string - otherNames: - description: |- - `otherNames` is an escape hatch for SAN that allows any type. We currently restrict the support to string like otherNames, cf RFC 5280 p 37 - Any UTF8 String valued otherName can be passed with by setting the keys oid: x.x.x.x and UTF8Value: somevalue for `otherName`. - Most commonly this would be UPN set with oid: 1.3.6.1.4.1.311.20.2.3 - You should ensure that any OID passed is valid for the UTF8String type as we do not explicitly validate this. - type: array - items: - type: object - properties: - oid: - description: |- - OID is the object identifier for the otherName SAN. - The object identifier must be expressed as a dotted string, for - example, "1.2.840.113556.1.4.221". - type: string - utf8Value: - description: |- - utf8Value is the string value of the otherName SAN. - The utf8Value accepts any valid UTF8 string to set as value for the otherName SAN. - type: string - privateKey: - description: |- - Private key options. These include the key algorithm and size, the used - encoding and the rotation policy. - type: object - properties: - algorithm: - description: |- - Algorithm is the private key algorithm of the corresponding private key - for this certificate. - - If provided, allowed values are either `RSA`, `ECDSA` or `Ed25519`. - If `algorithm` is specified and `size` is not provided, - key size of 2048 will be used for `RSA` key algorithm and - key size of 256 will be used for `ECDSA` key algorithm. - key size is ignored when using the `Ed25519` key algorithm. - type: string - enum: - - RSA - - ECDSA - - Ed25519 - encoding: - description: |- - The private key cryptography standards (PKCS) encoding for this - certificate's private key to be encoded in. - - If provided, allowed values are `PKCS1` and `PKCS8` standing for PKCS#1 - and PKCS#8, respectively. - Defaults to `PKCS1` if not specified. - type: string - enum: - - PKCS1 - - PKCS8 - rotationPolicy: - description: |- - RotationPolicy controls how private keys should be regenerated when a - re-issuance is being processed. - - If set to `Never`, a private key will only be generated if one does not - already exist in the target `spec.secretName`. If one does exist but it - does not have the correct algorithm or size, a warning will be raised - to await user intervention. - If set to `Always`, a private key matching the specified requirements - will be generated whenever a re-issuance occurs. - Default is `Never` for backward compatibility. - type: string - enum: - - Never - - Always - size: - description: |- - Size is the key bit size of the corresponding private key for this certificate. - - If `algorithm` is set to `RSA`, valid values are `2048`, `4096` or `8192`, - and will default to `2048` if not specified. - If `algorithm` is set to `ECDSA`, valid values are `256`, `384` or `521`, - and will default to `256` if not specified. - If `algorithm` is set to `Ed25519`, Size is ignored. - No other values are allowed. - type: integer - renewBefore: - description: |- - How long before the currently issued certificate's expiry cert-manager should - renew the certificate. For example, if a certificate is valid for 60 minutes, - and `renewBefore=10m`, cert-manager will begin to attempt to renew the certificate - 50 minutes after it was issued (i.e. when there are 10 minutes remaining until - the certificate is no longer valid). - - NOTE: The actual lifetime of the issued certificate is used to determine the - renewal time. If an issuer returns a certificate with a different lifetime than - the one requested, cert-manager will use the lifetime of the issued certificate. - - If unset, this defaults to 1/3 of the issued certificate's lifetime. - Minimum accepted value is 5 minutes. - Value must be in units accepted by Go time.ParseDuration https://golang.org/pkg/time/#ParseDuration. - Cannot be set if the `renewBeforePercentage` field is set. - type: string - renewBeforePercentage: - description: |- - `renewBeforePercentage` is like `renewBefore`, except it is a relative percentage - rather than an absolute duration. For example, if a certificate is valid for 60 - minutes, and `renewBeforePercentage=25`, cert-manager will begin to attempt to - renew the certificate 45 minutes after it was issued (i.e. when there are 15 - minutes (25%) remaining until the certificate is no longer valid). - - NOTE: The actual lifetime of the issued certificate is used to determine the - renewal time. If an issuer returns a certificate with a different lifetime than - the one requested, cert-manager will use the lifetime of the issued certificate. - - Value must be an integer in the range (0,100). The minimum effective - `renewBefore` derived from the `renewBeforePercentage` and `duration` fields is 5 - minutes. - Cannot be set if the `renewBefore` field is set. - type: integer - format: int32 - revisionHistoryLimit: - description: |- - The maximum number of CertificateRequest revisions that are maintained in - the Certificate's history. Each revision represents a single `CertificateRequest` - created by this Certificate, either when it was created, renewed, or Spec - was changed. Revisions will be removed by oldest first if the number of - revisions exceeds this number. - - If set, revisionHistoryLimit must be a value of `1` or greater. - If unset (`nil`), revisions will not be garbage collected. - Default value is `nil`. - type: integer - format: int32 - secretName: - description: |- - Name of the Secret resource that will be automatically created and - managed by this Certificate resource. It will be populated with a - private key and certificate, signed by the denoted issuer. The Secret - resource lives in the same namespace as the Certificate resource. - type: string - secretTemplate: - description: |- - Defines annotations and labels to be copied to the Certificate's Secret. - Labels and annotations on the Secret will be changed as they appear on the - SecretTemplate when added or removed. SecretTemplate annotations are added - in conjunction with, and cannot overwrite, the base set of annotations - cert-manager sets on the Certificate's Secret. - type: object - properties: - annotations: - description: Annotations is a key value map to be copied to the target Kubernetes Secret. - type: object - additionalProperties: - type: string - labels: - description: Labels is a key value map to be copied to the target Kubernetes Secret. - type: object - additionalProperties: - type: string - subject: - description: |- - Requested set of X509 certificate subject attributes. - More info: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.6 - - The common name attribute is specified separately in the `commonName` field. - Cannot be set if the `literalSubject` field is set. - type: object - properties: - countries: - description: Countries to be used on the Certificate. - type: array - items: - type: string - localities: - description: Cities to be used on the Certificate. - type: array - items: - type: string - organizationalUnits: - description: Organizational Units to be used on the Certificate. - type: array - items: - type: string - organizations: - description: Organizations to be used on the Certificate. - type: array - items: - type: string - postalCodes: - description: Postal codes to be used on the Certificate. - type: array - items: - type: string - provinces: - description: State/Provinces to be used on the Certificate. - type: array - items: - type: string - serialNumber: - description: Serial number to be used on the Certificate. - type: string - streetAddresses: - description: Street addresses to be used on the Certificate. - type: array - items: - type: string - uris: - description: Requested URI subject alternative names. - type: array - items: - type: string - usages: - description: |- - Requested key usages and extended key usages. - These usages are used to set the `usages` field on the created CertificateRequest - resources. If `encodeUsagesInRequest` is unset or set to `true`, the usages - will additionally be encoded in the `request` field which contains the CSR blob. - - If unset, defaults to `digital signature` and `key encipherment`. - type: array - items: - description: |- - KeyUsage specifies valid usage contexts for keys. - See: - https://tools.ietf.org/html/rfc5280#section-4.2.1.3 - https://tools.ietf.org/html/rfc5280#section-4.2.1.12 - - Valid KeyUsage values are as follows: - "signing", - "digital signature", - "content commitment", - "key encipherment", - "key agreement", - "data encipherment", - "cert sign", - "crl sign", - "encipher only", - "decipher only", - "any", - "server auth", - "client auth", - "code signing", - "email protection", - "s/mime", - "ipsec end system", - "ipsec tunnel", - "ipsec user", - "timestamping", - "ocsp signing", - "microsoft sgc", - "netscape sgc" - type: string - enum: - - signing - - digital signature - - content commitment - - key encipherment - - key agreement - - data encipherment - - cert sign - - crl sign - - encipher only - - decipher only - - any - - server auth - - client auth - - code signing - - email protection - - s/mime - - ipsec end system - - ipsec tunnel - - ipsec user - - timestamping - - ocsp signing - - microsoft sgc - - netscape sgc - status: - description: |- - Status of the Certificate. - This is set and managed automatically. - Read-only. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status - type: object - properties: - conditions: - description: |- - List of status conditions to indicate the status of certificates. - Known condition types are `Ready` and `Issuing`. - type: array - items: - description: CertificateCondition contains condition information for a Certificate. - type: object - required: - - status - - type - properties: - lastTransitionTime: - description: |- - LastTransitionTime is the timestamp corresponding to the last status - change of this condition. - type: string - format: date-time - message: - description: |- - Message is a human readable description of the details of the last - transition, complementing reason. - type: string - observedGeneration: - description: |- - If set, this represents the .metadata.generation that the condition was - set based upon. - For instance, if .metadata.generation is currently 12, but the - .status.condition[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the Certificate. - type: integer - format: int64 - reason: - description: |- - Reason is a brief machine readable explanation for the condition's last - transition. - type: string - status: - description: Status of the condition, one of (`True`, `False`, `Unknown`). - type: string - enum: - - "True" - - "False" - - Unknown - type: - description: Type of the condition, known values are (`Ready`, `Issuing`). - type: string - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - failedIssuanceAttempts: - description: |- - The number of continuous failed issuance attempts up till now. This - field gets removed (if set) on a successful issuance and gets set to - 1 if unset and an issuance has failed. If an issuance has failed, the - delay till the next issuance will be calculated using formula - time.Hour * 2 ^ (failedIssuanceAttempts - 1). - type: integer - lastFailureTime: - description: |- - LastFailureTime is set only if the latest issuance for this - Certificate failed and contains the time of the failure. If an - issuance has failed, the delay till the next issuance will be - calculated using formula time.Hour * 2 ^ (failedIssuanceAttempts - - 1). If the latest issuance has succeeded this field will be unset. - type: string - format: date-time - nextPrivateKeySecretName: - description: |- - The name of the Secret resource containing the private key to be used - for the next certificate iteration. - The keymanager controller will automatically set this field if the - `Issuing` condition is set to `True`. - It will automatically unset this field when the Issuing condition is - not set or False. - type: string - notAfter: - description: |- - The expiration time of the certificate stored in the secret named - by this resource in `spec.secretName`. - type: string - format: date-time - notBefore: - description: |- - The time after which the certificate stored in the secret named - by this resource in `spec.secretName` is valid. - type: string - format: date-time - renewalTime: - description: |- - RenewalTime is the time at which the certificate will be next - renewed. - If not set, no upcoming renewal is scheduled. - type: string - format: date-time - revision: - description: |- - The current 'revision' of the certificate as issued. - - When a CertificateRequest resource is created, it will have the - `cert-manager.io/certificate-revision` set to one greater than the - current value of this field. - - Upon issuance, this field will be set to the value of the annotation - on the CertificateRequest resource used to issue the certificate. - - Persisting the value on the CertificateRequest resource allows the - certificates controller to know whether a request is part of an old - issuance or if it is part of the ongoing revision's issuance by - checking if the revision value in the annotation is greater than this - field. - type: integer - served: true - storage: true - -# END crd {{- end }} - ---- -# START crd {{- if or .Values.crds.enabled .Values.installCRDs }} -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: challenges.acme.cert-manager.io - # START annotations {{- if .Values.crds.keep }} - annotations: - helm.sh/resource-policy: keep - # END annotations {{- end }} - labels: - app: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/name: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/instance: '{{ .Release.Name }}' - # Generated labels {{- include "labels" . | nindent 4 }} -spec: - group: acme.cert-manager.io - names: - kind: Challenge - listKind: ChallengeList - plural: challenges - singular: challenge - categories: - - cert-manager - - cert-manager-acme - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.state - name: State - type: string - - jsonPath: .spec.dnsName - name: Domain - type: string - - jsonPath: .status.reason - name: Reason - priority: 1 - type: string - - description: CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC. - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1 - schema: - openAPIV3Schema: - description: Challenge is a type to represent a Challenge request with an ACME server - type: object - required: - - metadata - - spec - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - type: object - required: - - authorizationURL - - dnsName - - issuerRef - - key - - solver - - token - - type - - url - properties: - authorizationURL: - description: |- - The URL to the ACME Authorization resource that this - challenge is a part of. - type: string - dnsName: - description: |- - dnsName is the identifier that this challenge is for, e.g. example.com. - If the requested DNSName is a 'wildcard', this field MUST be set to the - non-wildcard domain, e.g. for `*.example.com`, it must be `example.com`. - type: string - issuerRef: - description: |- - References a properly configured ACME-type Issuer which should - be used to create this Challenge. - If the Issuer does not exist, processing will be retried. - If the Issuer is not an 'ACME' Issuer, an error will be returned and the - Challenge will be marked as failed. - type: object - required: - - name - properties: - group: - description: Group of the resource being referred to. - type: string - kind: - description: Kind of the resource being referred to. - type: string - name: - description: Name of the resource being referred to. - type: string - key: - description: |- - The ACME challenge key for this challenge - For HTTP01 challenges, this is the value that must be responded with to - complete the HTTP01 challenge in the format: - `.`. - For DNS01 challenges, this is the base64 encoded SHA256 sum of the - `.` - text that must be set as the TXT record content. - type: string - solver: - description: |- - Contains the domain solving configuration that should be used to - solve this challenge resource. - type: object - properties: - dns01: - description: |- - Configures cert-manager to attempt to complete authorizations by - performing the DNS01 challenge flow. - type: object - properties: - acmeDNS: - description: |- - Use the 'ACME DNS' (https://github.com/joohoi/acme-dns) API to manage - DNS01 challenge records. - type: object - required: - - accountSecretRef - - host - properties: - accountSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - host: - type: string - akamai: - description: Use the Akamai DNS zone management API to manage DNS01 challenge records. - type: object - required: - - accessTokenSecretRef - - clientSecretSecretRef - - clientTokenSecretRef - - serviceConsumerDomain - properties: - accessTokenSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientSecretSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientTokenSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - serviceConsumerDomain: - type: string - azureDNS: - description: Use the Microsoft Azure DNS API to manage DNS01 challenge records. - type: object - required: - - resourceGroupName - - subscriptionID - properties: - clientID: - description: |- - Auth: Azure Service Principal: - The ClientID of the Azure Service Principal used to authenticate with Azure DNS. - If set, ClientSecret and TenantID must also be set. - type: string - clientSecretSecretRef: - description: |- - Auth: Azure Service Principal: - A reference to a Secret containing the password associated with the Service Principal. - If set, ClientID and TenantID must also be set. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - environment: - description: name of the Azure environment (default AzurePublicCloud) - type: string - enum: - - AzurePublicCloud - - AzureChinaCloud - - AzureGermanCloud - - AzureUSGovernmentCloud - hostedZoneName: - description: name of the DNS zone that should be used - type: string - managedIdentity: - description: |- - Auth: Azure Workload Identity or Azure Managed Service Identity: - Settings to enable Azure Workload Identity or Azure Managed Service Identity - If set, ClientID, ClientSecret and TenantID must not be set. - type: object - properties: - clientID: - description: client ID of the managed identity, can not be used at the same time as resourceID - type: string - resourceID: - description: |- - resource ID of the managed identity, can not be used at the same time as clientID - Cannot be used for Azure Managed Service Identity - type: string - resourceGroupName: - description: resource group the DNS zone is located in - type: string - subscriptionID: - description: ID of the Azure subscription - type: string - tenantID: - description: |- - Auth: Azure Service Principal: - The TenantID of the Azure Service Principal used to authenticate with Azure DNS. - If set, ClientID and ClientSecret must also be set. - type: string - cloudDNS: - description: Use the Google Cloud DNS API to manage DNS01 challenge records. - type: object - required: - - project - properties: - hostedZoneName: - description: |- - HostedZoneName is an optional field that tells cert-manager in which - Cloud DNS zone the challenge record has to be created. - If left empty cert-manager will automatically choose a zone. - type: string - project: - type: string - serviceAccountSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - cloudflare: - description: Use the Cloudflare API to manage DNS01 challenge records. - type: object - properties: - apiKeySecretRef: - description: |- - API key to use to authenticate with Cloudflare. - Note: using an API token to authenticate is now the recommended method - as it allows greater control of permissions. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - apiTokenSecretRef: - description: API token used to authenticate with Cloudflare. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - email: - description: Email of the account, only required when using API key based authentication. - type: string - cnameStrategy: - description: |- - CNAMEStrategy configures how the DNS01 provider should handle CNAME - records when found in DNS zones. - type: string - enum: - - None - - Follow - digitalocean: - description: Use the DigitalOcean DNS API to manage DNS01 challenge records. - type: object - required: - - tokenSecretRef - properties: - tokenSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - rfc2136: - description: |- - Use RFC2136 ("Dynamic Updates in the Domain Name System") (https://datatracker.ietf.org/doc/rfc2136/) - to manage DNS01 challenge records. - type: object - required: - - nameserver - properties: - nameserver: - description: |- - The IP address or hostname of an authoritative DNS server supporting - RFC2136 in the form host:port. If the host is an IPv6 address it must be - enclosed in square brackets (e.g [2001:db8::1]) ; port is optional. - This field is required. - type: string - tsigAlgorithm: - description: |- - The TSIG Algorithm configured in the DNS supporting RFC2136. Used only - when ``tsigSecretSecretRef`` and ``tsigKeyName`` are defined. - Supported values are (case-insensitive): ``HMACMD5`` (default), - ``HMACSHA1``, ``HMACSHA256`` or ``HMACSHA512``. - type: string - tsigKeyName: - description: |- - The TSIG Key name configured in the DNS. - If ``tsigSecretSecretRef`` is defined, this field is required. - type: string - tsigSecretSecretRef: - description: |- - The name of the secret containing the TSIG value. - If ``tsigKeyName`` is defined, this field is required. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - route53: - description: Use the AWS Route53 API to manage DNS01 challenge records. - type: object - properties: - accessKeyID: - description: |- - The AccessKeyID is used for authentication. - Cannot be set when SecretAccessKeyID is set. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, - see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials - type: string - accessKeyIDSecretRef: - description: |- - The SecretAccessKey is used for authentication. If set, pull the AWS - access key ID from a key within a Kubernetes Secret. - Cannot be set when AccessKeyID is set. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, - see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - auth: - description: Auth configures how cert-manager authenticates. - type: object - required: - - kubernetes - properties: - kubernetes: - description: |- - Kubernetes authenticates with Route53 using AssumeRoleWithWebIdentity - by passing a bound ServiceAccount token. - type: object - required: - - serviceAccountRef - properties: - serviceAccountRef: - description: |- - A reference to a service account that will be used to request a bound - token (also known as "projected token"). To use this field, you must - configure an RBAC rule to let cert-manager request a token. - type: object - required: - - name - properties: - audiences: - description: |- - TokenAudiences is an optional list of audiences to include in the - token passed to AWS. The default token consisting of the issuer's namespace - and name is always included. - If unset the audience defaults to `sts.amazonaws.com`. - type: array - items: - type: string - name: - description: Name of the ServiceAccount used to request a token. - type: string - hostedZoneID: - description: If set, the provider will manage only this zone in Route53 and will not do a lookup using the route53:ListHostedZonesByName api call. - type: string - region: - description: |- - Override the AWS region. - - Route53 is a global service and does not have regional endpoints but the - region specified here (or via environment variables) is used as a hint to - help compute the correct AWS credential scope and partition when it - connects to Route53. See: - - [Amazon Route 53 endpoints and quotas](https://docs.aws.amazon.com/general/latest/gr/r53.html) - - [Global services](https://docs.aws.amazon.com/whitepapers/latest/aws-fault-isolation-boundaries/global-services.html) - - If you omit this region field, cert-manager will use the region from - AWS_REGION and AWS_DEFAULT_REGION environment variables, if they are set - in the cert-manager controller Pod. - - The `region` field is not needed if you use [IAM Roles for Service Accounts (IRSA)](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html). - Instead an AWS_REGION environment variable is added to the cert-manager controller Pod by: - [Amazon EKS Pod Identity Webhook](https://github.com/aws/amazon-eks-pod-identity-webhook). - In this case this `region` field value is ignored. - - The `region` field is not needed if you use [EKS Pod Identities](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html). - Instead an AWS_REGION environment variable is added to the cert-manager controller Pod by: - [Amazon EKS Pod Identity Agent](https://github.com/aws/eks-pod-identity-agent), - In this case this `region` field value is ignored. - type: string - role: - description: |- - Role is a Role ARN which the Route53 provider will assume using either the explicit credentials AccessKeyID/SecretAccessKey - or the inferred credentials from environment variables, shared credentials file or AWS Instance metadata - type: string - secretAccessKeySecretRef: - description: |- - The SecretAccessKey is used for authentication. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, - see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - webhook: - description: |- - Configure an external webhook based DNS01 challenge solver to manage - DNS01 challenge records. - type: object - required: - - groupName - - solverName - properties: - config: - description: |- - Additional configuration that should be passed to the webhook apiserver - when challenges are processed. - This can contain arbitrary JSON data. - Secret values should not be specified in this stanza. - If secret values are needed (e.g. credentials for a DNS service), you - should use a SecretKeySelector to reference a Secret resource. - For details on the schema of this field, consult the webhook provider - implementation's documentation. - x-kubernetes-preserve-unknown-fields: true - groupName: - description: |- - The API group name that should be used when POSTing ChallengePayload - resources to the webhook apiserver. - This should be the same as the GroupName specified in the webhook - provider implementation. - type: string - solverName: - description: |- - The name of the solver to use, as defined in the webhook provider - implementation. - This will typically be the name of the provider, e.g. 'cloudflare'. - type: string - http01: - description: |- - Configures cert-manager to attempt to complete authorizations by - performing the HTTP01 challenge flow. - It is not possible to obtain certificates for wildcard domain names - (e.g. `*.example.com`) using the HTTP01 challenge mechanism. - type: object - properties: - gatewayHTTPRoute: - description: |- - The Gateway API is a sig-network community API that models service networking - in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will - create HTTPRoutes with the specified labels in the same namespace as the challenge. - This solver is experimental, and fields / behaviour may change in the future. - type: object - properties: - labels: - description: |- - Custom labels that will be applied to HTTPRoutes created by cert-manager - while solving HTTP-01 challenges. - type: object - additionalProperties: - type: string - parentRefs: - description: |- - When solving an HTTP-01 challenge, cert-manager creates an HTTPRoute. - cert-manager needs to know which parentRefs should be used when creating - the HTTPRoute. Usually, the parentRef references a Gateway. See: - https://gateway-api.sigs.k8s.io/api-types/httproute/#attaching-to-gateways - type: array - items: - description: |- - ParentReference identifies an API object (usually a Gateway) that can be considered - a parent of this resource (usually a route). There are two kinds of parent resources - with "Core" support: - - * Gateway (Gateway conformance profile) - * Service (Mesh conformance profile, ClusterIP Services only) - - This API may be extended in the future to support additional kinds of parent - resources. - - The API object must be valid in the cluster; the Group and Kind must - be registered in the cluster for this reference to be valid. - type: object - required: - - name - properties: - group: - description: |- - Group is the group of the referent. - When unspecified, "gateway.networking.k8s.io" is inferred. - To set the core API group (such as for a "Service" kind referent), - Group must be explicitly set to "" (empty string). - - Support: Core - type: string - default: gateway.networking.k8s.io - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - kind: - description: |- - Kind is kind of the referent. - - There are two kinds of parent resources with "Core" support: - - * Gateway (Gateway conformance profile) - * Service (Mesh conformance profile, ClusterIP Services only) - - Support for other resources is Implementation-Specific. - type: string - default: Gateway - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - name: - description: |- - Name is the name of the referent. - - Support: Core - type: string - maxLength: 253 - minLength: 1 - namespace: - description: |- - Namespace is the namespace of the referent. When unspecified, this refers - to the local namespace of the Route. - - Note that there are specific rules for ParentRefs which cross namespace - boundaries. Cross-namespace references are only valid if they are explicitly - allowed by something in the namespace they are referring to. For example: - Gateway has the AllowedRoutes field, and ReferenceGrant provides a - generic way to enable any other kind of cross-namespace reference. - - - ParentRefs from a Route to a Service in the same namespace are "producer" - routes, which apply default routing rules to inbound connections from - any namespace to the Service. - - ParentRefs from a Route to a Service in a different namespace are - "consumer" routes, and these routing rules are only applied to outbound - connections originating from the same namespace as the Route, for which - the intended destination of the connections are a Service targeted as a - ParentRef of the Route. - - - Support: Core - type: string - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - port: - description: |- - Port is the network port this Route targets. It can be interpreted - differently based on the type of parent resource. - - When the parent resource is a Gateway, this targets all listeners - listening on the specified port that also support this kind of Route(and - select this Route). It's not recommended to set `Port` unless the - networking behaviors specified in a Route must apply to a specific port - as opposed to a listener(s) whose port(s) may be changed. When both Port - and SectionName are specified, the name and port of the selected listener - must match both specified values. - - - When the parent resource is a Service, this targets a specific port in the - Service spec. When both Port (experimental) and SectionName are specified, - the name and port of the selected port must match both specified values. - - - Implementations MAY choose to support other parent resources. - Implementations supporting other types of parent resources MUST clearly - document how/if Port is interpreted. - - For the purpose of status, an attachment is considered successful as - long as the parent resource accepts it partially. For example, Gateway - listeners can restrict which Routes can attach to them by Route kind, - namespace, or hostname. If 1 of 2 Gateway listeners accept attachment - from the referencing Route, the Route MUST be considered successfully - attached. If no Gateway listeners accept attachment from this Route, - the Route MUST be considered detached from the Gateway. - - Support: Extended - type: integer - format: int32 - maximum: 65535 - minimum: 1 - sectionName: - description: |- - SectionName is the name of a section within the target resource. In the - following resources, SectionName is interpreted as the following: - - * Gateway: Listener name. When both Port (experimental) and SectionName - are specified, the name and port of the selected listener must match - both specified values. - * Service: Port name. When both Port (experimental) and SectionName - are specified, the name and port of the selected listener must match - both specified values. - - Implementations MAY choose to support attaching Routes to other resources. - If that is the case, they MUST clearly document how SectionName is - interpreted. - - When unspecified (empty string), this will reference the entire resource. - For the purpose of status, an attachment is considered successful if at - least one section in the parent resource accepts it. For example, Gateway - listeners can restrict which Routes can attach to them by Route kind, - namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from - the referencing Route, the Route MUST be considered successfully - attached. If no Gateway listeners accept attachment from this Route, the - Route MUST be considered detached from the Gateway. - - Support: Core - type: string - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - podTemplate: - description: |- - Optional pod template used to configure the ACME challenge solver pods - used for HTTP01 challenges. - type: object - properties: - metadata: - description: |- - ObjectMeta overrides for the pod used to solve HTTP01 challenges. - Only the 'labels' and 'annotations' fields may be set. - If labels or annotations overlap with in-built values, the values here - will override the in-built values. - type: object - properties: - annotations: - description: Annotations that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - labels: - description: Labels that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - spec: - description: |- - PodSpec defines overrides for the HTTP01 challenge solver pod. - Check ACMEChallengeSolverHTTP01IngressPodSpec to find out currently supported fields. - All other fields will be ignored. - type: object - properties: - affinity: - description: If specified, the pod's scheduling constraints - type: object - properties: - nodeAffinity: - description: Describes node affinity scheduling rules for the pod. - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node matches the corresponding matchExpressions; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: |- - An empty preferred scheduling term matches all objects with implicit weight 0 - (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). - type: object - required: - - preference - - weight - properties: - preference: - description: A node selector term, associated with the corresponding weight. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - weight: - description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to an update), the system - may or may not try to eventually evict the pod from its node. - type: object - required: - - nodeSelectorTerms - properties: - nodeSelectorTerms: - description: Required. A list of node selector terms. The terms are ORed. - type: array - items: - description: |- - A null or empty node selector term matches no objects. The requirements of - them are ANDed. - The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - podAffinity: - description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - podAntiAffinity: - description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the anti-affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the anti-affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the anti-affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - imagePullSecrets: - description: If specified, the pod's imagePullSecrets - type: array - items: - description: |- - LocalObjectReference contains enough information to let you locate the - referenced object inside the same namespace. - type: object - properties: - name: - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - default: "" - x-kubernetes-map-type: atomic - nodeSelector: - description: |- - NodeSelector is a selector which must be true for the pod to fit on a node. - Selector which must match a node's labels for the pod to be scheduled on that node. - More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ - type: object - additionalProperties: - type: string - priorityClassName: - description: If specified, the pod's priorityClassName. - type: string - securityContext: - description: If specified, the pod's security context - type: object - properties: - fsGroup: - description: |- - A special supplemental group that applies to all containers in a pod. - Some volume types allow the Kubelet to change the ownership of that volume - to be owned by the pod: - - 1. The owning GID will be the FSGroup - 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) - 3. The permission bits are OR'd with rw-rw---- - - If unset, the Kubelet will not modify the ownership and permissions of any volume. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - fsGroupChangePolicy: - description: |- - fsGroupChangePolicy defines behavior of changing ownership and permission of the volume - before being exposed inside Pod. This field will only apply to - volume types which support fsGroup based ownership(and permissions). - It will have no effect on ephemeral volume types such as: secret, configmaps - and emptydir. - Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. - Note that this field cannot be set when spec.os.name is windows. - type: string - runAsGroup: - description: |- - The GID to run the entrypoint of the container process. - Uses runtime default if unset. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - runAsNonRoot: - description: |- - Indicates that the container must run as a non-root user. - If true, the Kubelet will validate the image at runtime to ensure that it - does not run as UID 0 (root) and fail to start the container if it does. - If unset or false, no such validation will be performed. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: |- - The UID to run the entrypoint of the container process. - Defaults to user specified in image metadata if unspecified. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - seLinuxOptions: - description: |- - The SELinux context to be applied to all containers. - If unspecified, the container runtime will allocate a random SELinux context for each - container. May also be set in SecurityContext. If set in - both SecurityContext and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. - Note that this field cannot be set when spec.os.name is windows. - type: object - properties: - level: - description: Level is SELinux level label that applies to the container. - type: string - role: - description: Role is a SELinux role label that applies to the container. - type: string - type: - description: Type is a SELinux type label that applies to the container. - type: string - user: - description: User is a SELinux user label that applies to the container. - type: string - seccompProfile: - description: |- - The seccomp options to use by the containers in this pod. - Note that this field cannot be set when spec.os.name is windows. - type: object - required: - - type - properties: - localhostProfile: - description: |- - localhostProfile indicates a profile defined in a file on the node should be used. - The profile must be preconfigured on the node to work. - Must be a descending path, relative to the kubelet's configured seccomp profile location. - Must be set if type is "Localhost". Must NOT be set for any other type. - type: string - type: - description: |- - type indicates which kind of seccomp profile will be applied. - Valid options are: - - Localhost - a profile defined in a file on the node should be used. - RuntimeDefault - the container runtime default profile should be used. - Unconfined - no profile should be applied. - type: string - supplementalGroups: - description: |- - A list of groups applied to the first process run in each container, in addition - to the container's primary GID, the fsGroup (if specified), and group memberships - defined in the container image for the uid of the container process. If unspecified, - no additional groups are added to any container. Note that group memberships - defined in the container image for the uid of the container process are still effective, - even if they are not included in this list. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - type: integer - format: int64 - sysctls: - description: |- - Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported - sysctls (by the container runtime) might fail to launch. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - description: Sysctl defines a kernel parameter to be set - type: object - required: - - name - - value - properties: - name: - description: Name of a property to set - type: string - value: - description: Value of a property to set - type: string - serviceAccountName: - description: If specified, the pod's service account - type: string - tolerations: - description: If specified, the pod's tolerations. - type: array - items: - description: |- - The pod this Toleration is attached to tolerates any taint that matches - the triple using the matching operator . - type: object - properties: - effect: - description: |- - Effect indicates the taint effect to match. Empty means match all taint effects. - When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: |- - Key is the taint key that the toleration applies to. Empty means match all taint keys. - If the key is empty, operator must be Exists; this combination means to match all values and all keys. - type: string - operator: - description: |- - Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. - Exists is equivalent to wildcard for value, so that a pod can - tolerate all taints of a particular category. - type: string - tolerationSeconds: - description: |- - TolerationSeconds represents the period of time the toleration (which must be - of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, - it is not set, which means tolerate the taint forever (do not evict). Zero and - negative values will be treated as 0 (evict immediately) by the system. - type: integer - format: int64 - value: - description: |- - Value is the taint value the toleration matches to. - If the operator is Exists, the value should be empty, otherwise just a regular string. - type: string - serviceType: - description: |- - Optional service type for Kubernetes solver service. Supported values - are NodePort or ClusterIP. If unset, defaults to NodePort. - type: string - ingress: - description: |- - The ingress based HTTP01 challenge solver will solve challenges by - creating or modifying Ingress resources in order to route requests for - '/.well-known/acme-challenge/XYZ' to 'challenge solver' pods that are - provisioned by cert-manager for each Challenge to be completed. - type: object - properties: - class: - description: |- - This field configures the annotation `kubernetes.io/ingress.class` when - creating Ingress resources to solve ACME challenges that use this - challenge solver. Only one of `class`, `name` or `ingressClassName` may - be specified. - type: string - ingressClassName: - description: |- - This field configures the field `ingressClassName` on the created Ingress - resources used to solve ACME challenges that use this challenge solver. - This is the recommended way of configuring the ingress class. Only one of - `class`, `name` or `ingressClassName` may be specified. - type: string - ingressTemplate: - description: |- - Optional ingress template used to configure the ACME challenge solver - ingress used for HTTP01 challenges. - type: object - properties: - metadata: - description: |- - ObjectMeta overrides for the ingress used to solve HTTP01 challenges. - Only the 'labels' and 'annotations' fields may be set. - If labels or annotations overlap with in-built values, the values here - will override the in-built values. - type: object - properties: - annotations: - description: Annotations that should be added to the created ACME HTTP01 solver ingress. - type: object - additionalProperties: - type: string - labels: - description: Labels that should be added to the created ACME HTTP01 solver ingress. - type: object - additionalProperties: - type: string - name: - description: |- - The name of the ingress resource that should have ACME challenge solving - routes inserted into it in order to solve HTTP01 challenges. - This is typically used in conjunction with ingress controllers like - ingress-gce, which maintains a 1:1 mapping between external IPs and - ingress resources. Only one of `class`, `name` or `ingressClassName` may - be specified. - type: string - podTemplate: - description: |- - Optional pod template used to configure the ACME challenge solver pods - used for HTTP01 challenges. - type: object - properties: - metadata: - description: |- - ObjectMeta overrides for the pod used to solve HTTP01 challenges. - Only the 'labels' and 'annotations' fields may be set. - If labels or annotations overlap with in-built values, the values here - will override the in-built values. - type: object - properties: - annotations: - description: Annotations that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - labels: - description: Labels that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - spec: - description: |- - PodSpec defines overrides for the HTTP01 challenge solver pod. - Check ACMEChallengeSolverHTTP01IngressPodSpec to find out currently supported fields. - All other fields will be ignored. - type: object - properties: - affinity: - description: If specified, the pod's scheduling constraints - type: object - properties: - nodeAffinity: - description: Describes node affinity scheduling rules for the pod. - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node matches the corresponding matchExpressions; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: |- - An empty preferred scheduling term matches all objects with implicit weight 0 - (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). - type: object - required: - - preference - - weight - properties: - preference: - description: A node selector term, associated with the corresponding weight. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - weight: - description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to an update), the system - may or may not try to eventually evict the pod from its node. - type: object - required: - - nodeSelectorTerms - properties: - nodeSelectorTerms: - description: Required. A list of node selector terms. The terms are ORed. - type: array - items: - description: |- - A null or empty node selector term matches no objects. The requirements of - them are ANDed. - The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - podAffinity: - description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - podAntiAffinity: - description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the anti-affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the anti-affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the anti-affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - imagePullSecrets: - description: If specified, the pod's imagePullSecrets - type: array - items: - description: |- - LocalObjectReference contains enough information to let you locate the - referenced object inside the same namespace. - type: object - properties: - name: - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - default: "" - x-kubernetes-map-type: atomic - nodeSelector: - description: |- - NodeSelector is a selector which must be true for the pod to fit on a node. - Selector which must match a node's labels for the pod to be scheduled on that node. - More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ - type: object - additionalProperties: - type: string - priorityClassName: - description: If specified, the pod's priorityClassName. - type: string - securityContext: - description: If specified, the pod's security context - type: object - properties: - fsGroup: - description: |- - A special supplemental group that applies to all containers in a pod. - Some volume types allow the Kubelet to change the ownership of that volume - to be owned by the pod: - - 1. The owning GID will be the FSGroup - 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) - 3. The permission bits are OR'd with rw-rw---- - - If unset, the Kubelet will not modify the ownership and permissions of any volume. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - fsGroupChangePolicy: - description: |- - fsGroupChangePolicy defines behavior of changing ownership and permission of the volume - before being exposed inside Pod. This field will only apply to - volume types which support fsGroup based ownership(and permissions). - It will have no effect on ephemeral volume types such as: secret, configmaps - and emptydir. - Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. - Note that this field cannot be set when spec.os.name is windows. - type: string - runAsGroup: - description: |- - The GID to run the entrypoint of the container process. - Uses runtime default if unset. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - runAsNonRoot: - description: |- - Indicates that the container must run as a non-root user. - If true, the Kubelet will validate the image at runtime to ensure that it - does not run as UID 0 (root) and fail to start the container if it does. - If unset or false, no such validation will be performed. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: |- - The UID to run the entrypoint of the container process. - Defaults to user specified in image metadata if unspecified. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - seLinuxOptions: - description: |- - The SELinux context to be applied to all containers. - If unspecified, the container runtime will allocate a random SELinux context for each - container. May also be set in SecurityContext. If set in - both SecurityContext and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. - Note that this field cannot be set when spec.os.name is windows. - type: object - properties: - level: - description: Level is SELinux level label that applies to the container. - type: string - role: - description: Role is a SELinux role label that applies to the container. - type: string - type: - description: Type is a SELinux type label that applies to the container. - type: string - user: - description: User is a SELinux user label that applies to the container. - type: string - seccompProfile: - description: |- - The seccomp options to use by the containers in this pod. - Note that this field cannot be set when spec.os.name is windows. - type: object - required: - - type - properties: - localhostProfile: - description: |- - localhostProfile indicates a profile defined in a file on the node should be used. - The profile must be preconfigured on the node to work. - Must be a descending path, relative to the kubelet's configured seccomp profile location. - Must be set if type is "Localhost". Must NOT be set for any other type. - type: string - type: - description: |- - type indicates which kind of seccomp profile will be applied. - Valid options are: - - Localhost - a profile defined in a file on the node should be used. - RuntimeDefault - the container runtime default profile should be used. - Unconfined - no profile should be applied. - type: string - supplementalGroups: - description: |- - A list of groups applied to the first process run in each container, in addition - to the container's primary GID, the fsGroup (if specified), and group memberships - defined in the container image for the uid of the container process. If unspecified, - no additional groups are added to any container. Note that group memberships - defined in the container image for the uid of the container process are still effective, - even if they are not included in this list. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - type: integer - format: int64 - sysctls: - description: |- - Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported - sysctls (by the container runtime) might fail to launch. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - description: Sysctl defines a kernel parameter to be set - type: object - required: - - name - - value - properties: - name: - description: Name of a property to set - type: string - value: - description: Value of a property to set - type: string - serviceAccountName: - description: If specified, the pod's service account - type: string - tolerations: - description: If specified, the pod's tolerations. - type: array - items: - description: |- - The pod this Toleration is attached to tolerates any taint that matches - the triple using the matching operator . - type: object - properties: - effect: - description: |- - Effect indicates the taint effect to match. Empty means match all taint effects. - When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: |- - Key is the taint key that the toleration applies to. Empty means match all taint keys. - If the key is empty, operator must be Exists; this combination means to match all values and all keys. - type: string - operator: - description: |- - Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. - Exists is equivalent to wildcard for value, so that a pod can - tolerate all taints of a particular category. - type: string - tolerationSeconds: - description: |- - TolerationSeconds represents the period of time the toleration (which must be - of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, - it is not set, which means tolerate the taint forever (do not evict). Zero and - negative values will be treated as 0 (evict immediately) by the system. - type: integer - format: int64 - value: - description: |- - Value is the taint value the toleration matches to. - If the operator is Exists, the value should be empty, otherwise just a regular string. - type: string - serviceType: - description: |- - Optional service type for Kubernetes solver service. Supported values - are NodePort or ClusterIP. If unset, defaults to NodePort. - type: string - selector: - description: |- - Selector selects a set of DNSNames on the Certificate resource that - should be solved using this challenge solver. - If not specified, the solver will be treated as the 'default' solver - with the lowest priority, i.e. if any other solver has a more specific - match, it will be used instead. - type: object - properties: - dnsNames: - description: |- - List of DNSNames that this solver will be used to solve. - If specified and a match is found, a dnsNames selector will take - precedence over a dnsZones selector. - If multiple solvers match with the same dnsNames value, the solver - with the most matching labels in matchLabels will be selected. - If neither has more matches, the solver defined earlier in the list - will be selected. - type: array - items: - type: string - dnsZones: - description: |- - List of DNSZones that this solver will be used to solve. - The most specific DNS zone match specified here will take precedence - over other DNS zone matches, so a solver specifying sys.example.com - will be selected over one specifying example.com for the domain - www.sys.example.com. - If multiple solvers match with the same dnsZones value, the solver - with the most matching labels in matchLabels will be selected. - If neither has more matches, the solver defined earlier in the list - will be selected. - type: array - items: - type: string - matchLabels: - description: |- - A label selector that is used to refine the set of certificate's that - this challenge solver will apply to. - type: object - additionalProperties: - type: string - token: - description: |- - The ACME challenge token for this challenge. - This is the raw value returned from the ACME server. - type: string - type: - description: |- - The type of ACME challenge this resource represents. - One of "HTTP-01" or "DNS-01". - type: string - enum: - - HTTP-01 - - DNS-01 - url: - description: |- - The URL of the ACME Challenge resource for this challenge. - This can be used to lookup details about the status of this challenge. - type: string - wildcard: - description: |- - wildcard will be true if this challenge is for a wildcard identifier, - for example '*.example.com'. - type: boolean - status: - type: object - properties: - presented: - description: |- - presented will be set to true if the challenge values for this challenge - are currently 'presented'. - This *does not* imply the self check is passing. Only that the values - have been 'submitted' for the appropriate challenge mechanism (i.e. the - DNS01 TXT record has been presented, or the HTTP01 configuration has been - configured). - type: boolean - processing: - description: |- - Used to denote whether this challenge should be processed or not. - This field will only be set to true by the 'scheduling' component. - It will only be set to false by the 'challenges' controller, after the - challenge has reached a final state or timed out. - If this field is set to false, the challenge controller will not take - any more action. - type: boolean - reason: - description: |- - Contains human readable information on why the Challenge is in the - current state. - type: string - state: - description: |- - Contains the current 'state' of the challenge. - If not set, the state of the challenge is unknown. - type: string - enum: - - valid - - ready - - pending - - processing - - invalid - - expired - - errored - served: true - storage: true - subresources: - status: {} - -# END crd {{- end }} - ---- -# START crd {{- if or .Values.crds.enabled .Values.installCRDs }} -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: clusterissuers.cert-manager.io - # START annotations {{- if .Values.crds.keep }} - annotations: - helm.sh/resource-policy: keep - # END annotations {{- end }} - labels: - app: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/name: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/instance: '{{ .Release.Name }}' - # Generated labels {{- include "labels" . | nindent 4 }} -spec: - group: cert-manager.io - names: - kind: ClusterIssuer - listKind: ClusterIssuerList - plural: clusterissuers - singular: clusterissuer - categories: - - cert-manager - scope: Cluster - versions: - - name: v1 - subresources: - status: {} - additionalPrinterColumns: - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - priority: 1 - type: string - - jsonPath: .metadata.creationTimestamp - description: CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC. - name: Age - type: date - schema: - openAPIV3Schema: - description: |- - A ClusterIssuer represents a certificate issuing authority which can be - referenced as part of `issuerRef` fields. - It is similar to an Issuer, however it is cluster-scoped and therefore can - be referenced by resources that exist in *any* namespace, not just the same - namespace as the referent. - type: object - required: - - spec - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: Desired state of the ClusterIssuer resource. - type: object - properties: - acme: - description: |- - ACME configures this issuer to communicate with a RFC8555 (ACME) server - to obtain signed x509 certificates. - type: object - required: - - privateKeySecretRef - - server - properties: - caBundle: - description: |- - Base64-encoded bundle of PEM CAs which can be used to validate the certificate - chain presented by the ACME server. - Mutually exclusive with SkipTLSVerify; prefer using CABundle to prevent various - kinds of security vulnerabilities. - If CABundle and SkipTLSVerify are unset, the system certificate bundle inside - the container is used to validate the TLS connection. - type: string - format: byte - disableAccountKeyGeneration: - description: |- - Enables or disables generating a new ACME account key. - If true, the Issuer resource will *not* request a new account but will expect - the account key to be supplied via an existing secret. - If false, the cert-manager system will generate a new ACME account key - for the Issuer. - Defaults to false. - type: boolean - email: - description: |- - Email is the email address to be associated with the ACME account. - This field is optional, but it is strongly recommended to be set. - It will be used to contact you in case of issues with your account or - certificates, including expiry notification emails. - This field may be updated after the account is initially registered. - type: string - enableDurationFeature: - description: |- - Enables requesting a Not After date on certificates that matches the - duration of the certificate. This is not supported by all ACME servers - like Let's Encrypt. If set to true when the ACME server does not support - it, it will create an error on the Order. - Defaults to false. - type: boolean - externalAccountBinding: - description: |- - ExternalAccountBinding is a reference to a CA external account of the ACME - server. - If set, upon registration cert-manager will attempt to associate the given - external account credentials with the registered ACME account. - type: object - required: - - keyID - - keySecretRef - properties: - keyAlgorithm: - description: |- - Deprecated: keyAlgorithm field exists for historical compatibility - reasons and should not be used. The algorithm is now hardcoded to HS256 - in golang/x/crypto/acme. - type: string - enum: - - HS256 - - HS384 - - HS512 - keyID: - description: keyID is the ID of the CA key that the External Account is bound to. - type: string - keySecretRef: - description: |- - keySecretRef is a Secret Key Selector referencing a data item in a Kubernetes - Secret which holds the symmetric MAC key of the External Account Binding. - The `key` is the index string that is paired with the key data in the - Secret and should not be confused with the key data itself, or indeed with - the External Account Binding keyID above. - The secret key stored in the Secret **must** be un-padded, base64 URL - encoded data. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - preferredChain: - description: |- - PreferredChain is the chain to use if the ACME server outputs multiple. - PreferredChain is no guarantee that this one gets delivered by the ACME - endpoint. - For example, for Let's Encrypt's DST crosssign you would use: - "DST Root CA X3" or "ISRG Root X1" for the newer Let's Encrypt root CA. - This value picks the first certificate bundle in the combined set of - ACME default and alternative chains that has a root-most certificate with - this value as its issuer's commonname. - type: string - maxLength: 64 - privateKeySecretRef: - description: |- - PrivateKey is the name of a Kubernetes Secret resource that will be used to - store the automatically generated ACME account private key. - Optionally, a `key` may be specified to select a specific entry within - the named Secret resource. - If `key` is not specified, a default of `tls.key` will be used. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - server: - description: |- - Server is the URL used to access the ACME server's 'directory' endpoint. - For example, for Let's Encrypt's staging endpoint, you would use: - "https://acme-staging-v02.api.letsencrypt.org/directory". - Only ACME v2 endpoints (i.e. RFC 8555) are supported. - type: string - skipTLSVerify: - description: |- - INSECURE: Enables or disables validation of the ACME server TLS certificate. - If true, requests to the ACME server will not have the TLS certificate chain - validated. - Mutually exclusive with CABundle; prefer using CABundle to prevent various - kinds of security vulnerabilities. - Only enable this option in development environments. - If CABundle and SkipTLSVerify are unset, the system certificate bundle inside - the container is used to validate the TLS connection. - Defaults to false. - type: boolean - solvers: - description: |- - Solvers is a list of challenge solvers that will be used to solve - ACME challenges for the matching domains. - Solver configurations must be provided in order to obtain certificates - from an ACME server. - For more information, see: https://cert-manager.io/docs/configuration/acme/ - type: array - items: - description: |- - An ACMEChallengeSolver describes how to solve ACME challenges for the issuer it is part of. - A selector may be provided to use different solving strategies for different DNS names. - Only one of HTTP01 or DNS01 must be provided. - type: object - properties: - dns01: - description: |- - Configures cert-manager to attempt to complete authorizations by - performing the DNS01 challenge flow. - type: object - properties: - acmeDNS: - description: |- - Use the 'ACME DNS' (https://github.com/joohoi/acme-dns) API to manage - DNS01 challenge records. - type: object - required: - - accountSecretRef - - host - properties: - accountSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - host: - type: string - akamai: - description: Use the Akamai DNS zone management API to manage DNS01 challenge records. - type: object - required: - - accessTokenSecretRef - - clientSecretSecretRef - - clientTokenSecretRef - - serviceConsumerDomain - properties: - accessTokenSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientSecretSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientTokenSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - serviceConsumerDomain: - type: string - azureDNS: - description: Use the Microsoft Azure DNS API to manage DNS01 challenge records. - type: object - required: - - resourceGroupName - - subscriptionID - properties: - clientID: - description: |- - Auth: Azure Service Principal: - The ClientID of the Azure Service Principal used to authenticate with Azure DNS. - If set, ClientSecret and TenantID must also be set. - type: string - clientSecretSecretRef: - description: |- - Auth: Azure Service Principal: - A reference to a Secret containing the password associated with the Service Principal. - If set, ClientID and TenantID must also be set. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - environment: - description: name of the Azure environment (default AzurePublicCloud) - type: string - enum: - - AzurePublicCloud - - AzureChinaCloud - - AzureGermanCloud - - AzureUSGovernmentCloud - hostedZoneName: - description: name of the DNS zone that should be used - type: string - managedIdentity: - description: |- - Auth: Azure Workload Identity or Azure Managed Service Identity: - Settings to enable Azure Workload Identity or Azure Managed Service Identity - If set, ClientID, ClientSecret and TenantID must not be set. - type: object - properties: - clientID: - description: client ID of the managed identity, can not be used at the same time as resourceID - type: string - resourceID: - description: |- - resource ID of the managed identity, can not be used at the same time as clientID - Cannot be used for Azure Managed Service Identity - type: string - resourceGroupName: - description: resource group the DNS zone is located in - type: string - subscriptionID: - description: ID of the Azure subscription - type: string - tenantID: - description: |- - Auth: Azure Service Principal: - The TenantID of the Azure Service Principal used to authenticate with Azure DNS. - If set, ClientID and ClientSecret must also be set. - type: string - cloudDNS: - description: Use the Google Cloud DNS API to manage DNS01 challenge records. - type: object - required: - - project - properties: - hostedZoneName: - description: |- - HostedZoneName is an optional field that tells cert-manager in which - Cloud DNS zone the challenge record has to be created. - If left empty cert-manager will automatically choose a zone. - type: string - project: - type: string - serviceAccountSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - cloudflare: - description: Use the Cloudflare API to manage DNS01 challenge records. - type: object - properties: - apiKeySecretRef: - description: |- - API key to use to authenticate with Cloudflare. - Note: using an API token to authenticate is now the recommended method - as it allows greater control of permissions. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - apiTokenSecretRef: - description: API token used to authenticate with Cloudflare. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - email: - description: Email of the account, only required when using API key based authentication. - type: string - cnameStrategy: - description: |- - CNAMEStrategy configures how the DNS01 provider should handle CNAME - records when found in DNS zones. - type: string - enum: - - None - - Follow - digitalocean: - description: Use the DigitalOcean DNS API to manage DNS01 challenge records. - type: object - required: - - tokenSecretRef - properties: - tokenSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - rfc2136: - description: |- - Use RFC2136 ("Dynamic Updates in the Domain Name System") (https://datatracker.ietf.org/doc/rfc2136/) - to manage DNS01 challenge records. - type: object - required: - - nameserver - properties: - nameserver: - description: |- - The IP address or hostname of an authoritative DNS server supporting - RFC2136 in the form host:port. If the host is an IPv6 address it must be - enclosed in square brackets (e.g [2001:db8::1]) ; port is optional. - This field is required. - type: string - tsigAlgorithm: - description: |- - The TSIG Algorithm configured in the DNS supporting RFC2136. Used only - when ``tsigSecretSecretRef`` and ``tsigKeyName`` are defined. - Supported values are (case-insensitive): ``HMACMD5`` (default), - ``HMACSHA1``, ``HMACSHA256`` or ``HMACSHA512``. - type: string - tsigKeyName: - description: |- - The TSIG Key name configured in the DNS. - If ``tsigSecretSecretRef`` is defined, this field is required. - type: string - tsigSecretSecretRef: - description: |- - The name of the secret containing the TSIG value. - If ``tsigKeyName`` is defined, this field is required. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - route53: - description: Use the AWS Route53 API to manage DNS01 challenge records. - type: object - properties: - accessKeyID: - description: |- - The AccessKeyID is used for authentication. - Cannot be set when SecretAccessKeyID is set. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, - see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials - type: string - accessKeyIDSecretRef: - description: |- - The SecretAccessKey is used for authentication. If set, pull the AWS - access key ID from a key within a Kubernetes Secret. - Cannot be set when AccessKeyID is set. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, - see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - auth: - description: Auth configures how cert-manager authenticates. - type: object - required: - - kubernetes - properties: - kubernetes: - description: |- - Kubernetes authenticates with Route53 using AssumeRoleWithWebIdentity - by passing a bound ServiceAccount token. - type: object - required: - - serviceAccountRef - properties: - serviceAccountRef: - description: |- - A reference to a service account that will be used to request a bound - token (also known as "projected token"). To use this field, you must - configure an RBAC rule to let cert-manager request a token. - type: object - required: - - name - properties: - audiences: - description: |- - TokenAudiences is an optional list of audiences to include in the - token passed to AWS. The default token consisting of the issuer's namespace - and name is always included. - If unset the audience defaults to `sts.amazonaws.com`. - type: array - items: - type: string - name: - description: Name of the ServiceAccount used to request a token. - type: string - hostedZoneID: - description: If set, the provider will manage only this zone in Route53 and will not do a lookup using the route53:ListHostedZonesByName api call. - type: string - region: - description: |- - Override the AWS region. - - Route53 is a global service and does not have regional endpoints but the - region specified here (or via environment variables) is used as a hint to - help compute the correct AWS credential scope and partition when it - connects to Route53. See: - - [Amazon Route 53 endpoints and quotas](https://docs.aws.amazon.com/general/latest/gr/r53.html) - - [Global services](https://docs.aws.amazon.com/whitepapers/latest/aws-fault-isolation-boundaries/global-services.html) - - If you omit this region field, cert-manager will use the region from - AWS_REGION and AWS_DEFAULT_REGION environment variables, if they are set - in the cert-manager controller Pod. - - The `region` field is not needed if you use [IAM Roles for Service Accounts (IRSA)](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html). - Instead an AWS_REGION environment variable is added to the cert-manager controller Pod by: - [Amazon EKS Pod Identity Webhook](https://github.com/aws/amazon-eks-pod-identity-webhook). - In this case this `region` field value is ignored. - - The `region` field is not needed if you use [EKS Pod Identities](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html). - Instead an AWS_REGION environment variable is added to the cert-manager controller Pod by: - [Amazon EKS Pod Identity Agent](https://github.com/aws/eks-pod-identity-agent), - In this case this `region` field value is ignored. - type: string - role: - description: |- - Role is a Role ARN which the Route53 provider will assume using either the explicit credentials AccessKeyID/SecretAccessKey - or the inferred credentials from environment variables, shared credentials file or AWS Instance metadata - type: string - secretAccessKeySecretRef: - description: |- - The SecretAccessKey is used for authentication. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, - see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - webhook: - description: |- - Configure an external webhook based DNS01 challenge solver to manage - DNS01 challenge records. - type: object - required: - - groupName - - solverName - properties: - config: - description: |- - Additional configuration that should be passed to the webhook apiserver - when challenges are processed. - This can contain arbitrary JSON data. - Secret values should not be specified in this stanza. - If secret values are needed (e.g. credentials for a DNS service), you - should use a SecretKeySelector to reference a Secret resource. - For details on the schema of this field, consult the webhook provider - implementation's documentation. - x-kubernetes-preserve-unknown-fields: true - groupName: - description: |- - The API group name that should be used when POSTing ChallengePayload - resources to the webhook apiserver. - This should be the same as the GroupName specified in the webhook - provider implementation. - type: string - solverName: - description: |- - The name of the solver to use, as defined in the webhook provider - implementation. - This will typically be the name of the provider, e.g. 'cloudflare'. - type: string - http01: - description: |- - Configures cert-manager to attempt to complete authorizations by - performing the HTTP01 challenge flow. - It is not possible to obtain certificates for wildcard domain names - (e.g. `*.example.com`) using the HTTP01 challenge mechanism. - type: object - properties: - gatewayHTTPRoute: - description: |- - The Gateway API is a sig-network community API that models service networking - in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will - create HTTPRoutes with the specified labels in the same namespace as the challenge. - This solver is experimental, and fields / behaviour may change in the future. - type: object - properties: - labels: - description: |- - Custom labels that will be applied to HTTPRoutes created by cert-manager - while solving HTTP-01 challenges. - type: object - additionalProperties: - type: string - parentRefs: - description: |- - When solving an HTTP-01 challenge, cert-manager creates an HTTPRoute. - cert-manager needs to know which parentRefs should be used when creating - the HTTPRoute. Usually, the parentRef references a Gateway. See: - https://gateway-api.sigs.k8s.io/api-types/httproute/#attaching-to-gateways - type: array - items: - description: |- - ParentReference identifies an API object (usually a Gateway) that can be considered - a parent of this resource (usually a route). There are two kinds of parent resources - with "Core" support: - - * Gateway (Gateway conformance profile) - * Service (Mesh conformance profile, ClusterIP Services only) - - This API may be extended in the future to support additional kinds of parent - resources. - - The API object must be valid in the cluster; the Group and Kind must - be registered in the cluster for this reference to be valid. - type: object - required: - - name - properties: - group: - description: |- - Group is the group of the referent. - When unspecified, "gateway.networking.k8s.io" is inferred. - To set the core API group (such as for a "Service" kind referent), - Group must be explicitly set to "" (empty string). - - Support: Core - type: string - default: gateway.networking.k8s.io - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - kind: - description: |- - Kind is kind of the referent. - - There are two kinds of parent resources with "Core" support: - - * Gateway (Gateway conformance profile) - * Service (Mesh conformance profile, ClusterIP Services only) - - Support for other resources is Implementation-Specific. - type: string - default: Gateway - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - name: - description: |- - Name is the name of the referent. - - Support: Core - type: string - maxLength: 253 - minLength: 1 - namespace: - description: |- - Namespace is the namespace of the referent. When unspecified, this refers - to the local namespace of the Route. - - Note that there are specific rules for ParentRefs which cross namespace - boundaries. Cross-namespace references are only valid if they are explicitly - allowed by something in the namespace they are referring to. For example: - Gateway has the AllowedRoutes field, and ReferenceGrant provides a - generic way to enable any other kind of cross-namespace reference. - - - ParentRefs from a Route to a Service in the same namespace are "producer" - routes, which apply default routing rules to inbound connections from - any namespace to the Service. - - ParentRefs from a Route to a Service in a different namespace are - "consumer" routes, and these routing rules are only applied to outbound - connections originating from the same namespace as the Route, for which - the intended destination of the connections are a Service targeted as a - ParentRef of the Route. - - - Support: Core - type: string - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - port: - description: |- - Port is the network port this Route targets. It can be interpreted - differently based on the type of parent resource. - - When the parent resource is a Gateway, this targets all listeners - listening on the specified port that also support this kind of Route(and - select this Route). It's not recommended to set `Port` unless the - networking behaviors specified in a Route must apply to a specific port - as opposed to a listener(s) whose port(s) may be changed. When both Port - and SectionName are specified, the name and port of the selected listener - must match both specified values. - - - When the parent resource is a Service, this targets a specific port in the - Service spec. When both Port (experimental) and SectionName are specified, - the name and port of the selected port must match both specified values. - - - Implementations MAY choose to support other parent resources. - Implementations supporting other types of parent resources MUST clearly - document how/if Port is interpreted. - - For the purpose of status, an attachment is considered successful as - long as the parent resource accepts it partially. For example, Gateway - listeners can restrict which Routes can attach to them by Route kind, - namespace, or hostname. If 1 of 2 Gateway listeners accept attachment - from the referencing Route, the Route MUST be considered successfully - attached. If no Gateway listeners accept attachment from this Route, - the Route MUST be considered detached from the Gateway. - - Support: Extended - type: integer - format: int32 - maximum: 65535 - minimum: 1 - sectionName: - description: |- - SectionName is the name of a section within the target resource. In the - following resources, SectionName is interpreted as the following: - - * Gateway: Listener name. When both Port (experimental) and SectionName - are specified, the name and port of the selected listener must match - both specified values. - * Service: Port name. When both Port (experimental) and SectionName - are specified, the name and port of the selected listener must match - both specified values. - - Implementations MAY choose to support attaching Routes to other resources. - If that is the case, they MUST clearly document how SectionName is - interpreted. - - When unspecified (empty string), this will reference the entire resource. - For the purpose of status, an attachment is considered successful if at - least one section in the parent resource accepts it. For example, Gateway - listeners can restrict which Routes can attach to them by Route kind, - namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from - the referencing Route, the Route MUST be considered successfully - attached. If no Gateway listeners accept attachment from this Route, the - Route MUST be considered detached from the Gateway. - - Support: Core - type: string - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - podTemplate: - description: |- - Optional pod template used to configure the ACME challenge solver pods - used for HTTP01 challenges. - type: object - properties: - metadata: - description: |- - ObjectMeta overrides for the pod used to solve HTTP01 challenges. - Only the 'labels' and 'annotations' fields may be set. - If labels or annotations overlap with in-built values, the values here - will override the in-built values. - type: object - properties: - annotations: - description: Annotations that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - labels: - description: Labels that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - spec: - description: |- - PodSpec defines overrides for the HTTP01 challenge solver pod. - Check ACMEChallengeSolverHTTP01IngressPodSpec to find out currently supported fields. - All other fields will be ignored. - type: object - properties: - affinity: - description: If specified, the pod's scheduling constraints - type: object - properties: - nodeAffinity: - description: Describes node affinity scheduling rules for the pod. - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node matches the corresponding matchExpressions; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: |- - An empty preferred scheduling term matches all objects with implicit weight 0 - (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). - type: object - required: - - preference - - weight - properties: - preference: - description: A node selector term, associated with the corresponding weight. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - weight: - description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to an update), the system - may or may not try to eventually evict the pod from its node. - type: object - required: - - nodeSelectorTerms - properties: - nodeSelectorTerms: - description: Required. A list of node selector terms. The terms are ORed. - type: array - items: - description: |- - A null or empty node selector term matches no objects. The requirements of - them are ANDed. - The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - podAffinity: - description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - podAntiAffinity: - description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the anti-affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the anti-affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the anti-affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - imagePullSecrets: - description: If specified, the pod's imagePullSecrets - type: array - items: - description: |- - LocalObjectReference contains enough information to let you locate the - referenced object inside the same namespace. - type: object - properties: - name: - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - default: "" - x-kubernetes-map-type: atomic - nodeSelector: - description: |- - NodeSelector is a selector which must be true for the pod to fit on a node. - Selector which must match a node's labels for the pod to be scheduled on that node. - More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ - type: object - additionalProperties: - type: string - priorityClassName: - description: If specified, the pod's priorityClassName. - type: string - securityContext: - description: If specified, the pod's security context - type: object - properties: - fsGroup: - description: |- - A special supplemental group that applies to all containers in a pod. - Some volume types allow the Kubelet to change the ownership of that volume - to be owned by the pod: - - 1. The owning GID will be the FSGroup - 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) - 3. The permission bits are OR'd with rw-rw---- - - If unset, the Kubelet will not modify the ownership and permissions of any volume. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - fsGroupChangePolicy: - description: |- - fsGroupChangePolicy defines behavior of changing ownership and permission of the volume - before being exposed inside Pod. This field will only apply to - volume types which support fsGroup based ownership(and permissions). - It will have no effect on ephemeral volume types such as: secret, configmaps - and emptydir. - Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. - Note that this field cannot be set when spec.os.name is windows. - type: string - runAsGroup: - description: |- - The GID to run the entrypoint of the container process. - Uses runtime default if unset. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - runAsNonRoot: - description: |- - Indicates that the container must run as a non-root user. - If true, the Kubelet will validate the image at runtime to ensure that it - does not run as UID 0 (root) and fail to start the container if it does. - If unset or false, no such validation will be performed. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: |- - The UID to run the entrypoint of the container process. - Defaults to user specified in image metadata if unspecified. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - seLinuxOptions: - description: |- - The SELinux context to be applied to all containers. - If unspecified, the container runtime will allocate a random SELinux context for each - container. May also be set in SecurityContext. If set in - both SecurityContext and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. - Note that this field cannot be set when spec.os.name is windows. - type: object - properties: - level: - description: Level is SELinux level label that applies to the container. - type: string - role: - description: Role is a SELinux role label that applies to the container. - type: string - type: - description: Type is a SELinux type label that applies to the container. - type: string - user: - description: User is a SELinux user label that applies to the container. - type: string - seccompProfile: - description: |- - The seccomp options to use by the containers in this pod. - Note that this field cannot be set when spec.os.name is windows. - type: object - required: - - type - properties: - localhostProfile: - description: |- - localhostProfile indicates a profile defined in a file on the node should be used. - The profile must be preconfigured on the node to work. - Must be a descending path, relative to the kubelet's configured seccomp profile location. - Must be set if type is "Localhost". Must NOT be set for any other type. - type: string - type: - description: |- - type indicates which kind of seccomp profile will be applied. - Valid options are: - - Localhost - a profile defined in a file on the node should be used. - RuntimeDefault - the container runtime default profile should be used. - Unconfined - no profile should be applied. - type: string - supplementalGroups: - description: |- - A list of groups applied to the first process run in each container, in addition - to the container's primary GID, the fsGroup (if specified), and group memberships - defined in the container image for the uid of the container process. If unspecified, - no additional groups are added to any container. Note that group memberships - defined in the container image for the uid of the container process are still effective, - even if they are not included in this list. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - type: integer - format: int64 - sysctls: - description: |- - Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported - sysctls (by the container runtime) might fail to launch. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - description: Sysctl defines a kernel parameter to be set - type: object - required: - - name - - value - properties: - name: - description: Name of a property to set - type: string - value: - description: Value of a property to set - type: string - serviceAccountName: - description: If specified, the pod's service account - type: string - tolerations: - description: If specified, the pod's tolerations. - type: array - items: - description: |- - The pod this Toleration is attached to tolerates any taint that matches - the triple using the matching operator . - type: object - properties: - effect: - description: |- - Effect indicates the taint effect to match. Empty means match all taint effects. - When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: |- - Key is the taint key that the toleration applies to. Empty means match all taint keys. - If the key is empty, operator must be Exists; this combination means to match all values and all keys. - type: string - operator: - description: |- - Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. - Exists is equivalent to wildcard for value, so that a pod can - tolerate all taints of a particular category. - type: string - tolerationSeconds: - description: |- - TolerationSeconds represents the period of time the toleration (which must be - of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, - it is not set, which means tolerate the taint forever (do not evict). Zero and - negative values will be treated as 0 (evict immediately) by the system. - type: integer - format: int64 - value: - description: |- - Value is the taint value the toleration matches to. - If the operator is Exists, the value should be empty, otherwise just a regular string. - type: string - serviceType: - description: |- - Optional service type for Kubernetes solver service. Supported values - are NodePort or ClusterIP. If unset, defaults to NodePort. - type: string - ingress: - description: |- - The ingress based HTTP01 challenge solver will solve challenges by - creating or modifying Ingress resources in order to route requests for - '/.well-known/acme-challenge/XYZ' to 'challenge solver' pods that are - provisioned by cert-manager for each Challenge to be completed. - type: object - properties: - class: - description: |- - This field configures the annotation `kubernetes.io/ingress.class` when - creating Ingress resources to solve ACME challenges that use this - challenge solver. Only one of `class`, `name` or `ingressClassName` may - be specified. - type: string - ingressClassName: - description: |- - This field configures the field `ingressClassName` on the created Ingress - resources used to solve ACME challenges that use this challenge solver. - This is the recommended way of configuring the ingress class. Only one of - `class`, `name` or `ingressClassName` may be specified. - type: string - ingressTemplate: - description: |- - Optional ingress template used to configure the ACME challenge solver - ingress used for HTTP01 challenges. - type: object - properties: - metadata: - description: |- - ObjectMeta overrides for the ingress used to solve HTTP01 challenges. - Only the 'labels' and 'annotations' fields may be set. - If labels or annotations overlap with in-built values, the values here - will override the in-built values. - type: object - properties: - annotations: - description: Annotations that should be added to the created ACME HTTP01 solver ingress. - type: object - additionalProperties: - type: string - labels: - description: Labels that should be added to the created ACME HTTP01 solver ingress. - type: object - additionalProperties: - type: string - name: - description: |- - The name of the ingress resource that should have ACME challenge solving - routes inserted into it in order to solve HTTP01 challenges. - This is typically used in conjunction with ingress controllers like - ingress-gce, which maintains a 1:1 mapping between external IPs and - ingress resources. Only one of `class`, `name` or `ingressClassName` may - be specified. - type: string - podTemplate: - description: |- - Optional pod template used to configure the ACME challenge solver pods - used for HTTP01 challenges. - type: object - properties: - metadata: - description: |- - ObjectMeta overrides for the pod used to solve HTTP01 challenges. - Only the 'labels' and 'annotations' fields may be set. - If labels or annotations overlap with in-built values, the values here - will override the in-built values. - type: object - properties: - annotations: - description: Annotations that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - labels: - description: Labels that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - spec: - description: |- - PodSpec defines overrides for the HTTP01 challenge solver pod. - Check ACMEChallengeSolverHTTP01IngressPodSpec to find out currently supported fields. - All other fields will be ignored. - type: object - properties: - affinity: - description: If specified, the pod's scheduling constraints - type: object - properties: - nodeAffinity: - description: Describes node affinity scheduling rules for the pod. - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node matches the corresponding matchExpressions; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: |- - An empty preferred scheduling term matches all objects with implicit weight 0 - (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). - type: object - required: - - preference - - weight - properties: - preference: - description: A node selector term, associated with the corresponding weight. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - weight: - description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to an update), the system - may or may not try to eventually evict the pod from its node. - type: object - required: - - nodeSelectorTerms - properties: - nodeSelectorTerms: - description: Required. A list of node selector terms. The terms are ORed. - type: array - items: - description: |- - A null or empty node selector term matches no objects. The requirements of - them are ANDed. - The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - podAffinity: - description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - podAntiAffinity: - description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the anti-affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the anti-affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the anti-affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - imagePullSecrets: - description: If specified, the pod's imagePullSecrets - type: array - items: - description: |- - LocalObjectReference contains enough information to let you locate the - referenced object inside the same namespace. - type: object - properties: - name: - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - default: "" - x-kubernetes-map-type: atomic - nodeSelector: - description: |- - NodeSelector is a selector which must be true for the pod to fit on a node. - Selector which must match a node's labels for the pod to be scheduled on that node. - More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ - type: object - additionalProperties: - type: string - priorityClassName: - description: If specified, the pod's priorityClassName. - type: string - securityContext: - description: If specified, the pod's security context - type: object - properties: - fsGroup: - description: |- - A special supplemental group that applies to all containers in a pod. - Some volume types allow the Kubelet to change the ownership of that volume - to be owned by the pod: - - 1. The owning GID will be the FSGroup - 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) - 3. The permission bits are OR'd with rw-rw---- - - If unset, the Kubelet will not modify the ownership and permissions of any volume. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - fsGroupChangePolicy: - description: |- - fsGroupChangePolicy defines behavior of changing ownership and permission of the volume - before being exposed inside Pod. This field will only apply to - volume types which support fsGroup based ownership(and permissions). - It will have no effect on ephemeral volume types such as: secret, configmaps - and emptydir. - Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. - Note that this field cannot be set when spec.os.name is windows. - type: string - runAsGroup: - description: |- - The GID to run the entrypoint of the container process. - Uses runtime default if unset. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - runAsNonRoot: - description: |- - Indicates that the container must run as a non-root user. - If true, the Kubelet will validate the image at runtime to ensure that it - does not run as UID 0 (root) and fail to start the container if it does. - If unset or false, no such validation will be performed. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: |- - The UID to run the entrypoint of the container process. - Defaults to user specified in image metadata if unspecified. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - seLinuxOptions: - description: |- - The SELinux context to be applied to all containers. - If unspecified, the container runtime will allocate a random SELinux context for each - container. May also be set in SecurityContext. If set in - both SecurityContext and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. - Note that this field cannot be set when spec.os.name is windows. - type: object - properties: - level: - description: Level is SELinux level label that applies to the container. - type: string - role: - description: Role is a SELinux role label that applies to the container. - type: string - type: - description: Type is a SELinux type label that applies to the container. - type: string - user: - description: User is a SELinux user label that applies to the container. - type: string - seccompProfile: - description: |- - The seccomp options to use by the containers in this pod. - Note that this field cannot be set when spec.os.name is windows. - type: object - required: - - type - properties: - localhostProfile: - description: |- - localhostProfile indicates a profile defined in a file on the node should be used. - The profile must be preconfigured on the node to work. - Must be a descending path, relative to the kubelet's configured seccomp profile location. - Must be set if type is "Localhost". Must NOT be set for any other type. - type: string - type: - description: |- - type indicates which kind of seccomp profile will be applied. - Valid options are: - - Localhost - a profile defined in a file on the node should be used. - RuntimeDefault - the container runtime default profile should be used. - Unconfined - no profile should be applied. - type: string - supplementalGroups: - description: |- - A list of groups applied to the first process run in each container, in addition - to the container's primary GID, the fsGroup (if specified), and group memberships - defined in the container image for the uid of the container process. If unspecified, - no additional groups are added to any container. Note that group memberships - defined in the container image for the uid of the container process are still effective, - even if they are not included in this list. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - type: integer - format: int64 - sysctls: - description: |- - Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported - sysctls (by the container runtime) might fail to launch. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - description: Sysctl defines a kernel parameter to be set - type: object - required: - - name - - value - properties: - name: - description: Name of a property to set - type: string - value: - description: Value of a property to set - type: string - serviceAccountName: - description: If specified, the pod's service account - type: string - tolerations: - description: If specified, the pod's tolerations. - type: array - items: - description: |- - The pod this Toleration is attached to tolerates any taint that matches - the triple using the matching operator . - type: object - properties: - effect: - description: |- - Effect indicates the taint effect to match. Empty means match all taint effects. - When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: |- - Key is the taint key that the toleration applies to. Empty means match all taint keys. - If the key is empty, operator must be Exists; this combination means to match all values and all keys. - type: string - operator: - description: |- - Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. - Exists is equivalent to wildcard for value, so that a pod can - tolerate all taints of a particular category. - type: string - tolerationSeconds: - description: |- - TolerationSeconds represents the period of time the toleration (which must be - of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, - it is not set, which means tolerate the taint forever (do not evict). Zero and - negative values will be treated as 0 (evict immediately) by the system. - type: integer - format: int64 - value: - description: |- - Value is the taint value the toleration matches to. - If the operator is Exists, the value should be empty, otherwise just a regular string. - type: string - serviceType: - description: |- - Optional service type for Kubernetes solver service. Supported values - are NodePort or ClusterIP. If unset, defaults to NodePort. - type: string - selector: - description: |- - Selector selects a set of DNSNames on the Certificate resource that - should be solved using this challenge solver. - If not specified, the solver will be treated as the 'default' solver - with the lowest priority, i.e. if any other solver has a more specific - match, it will be used instead. - type: object - properties: - dnsNames: - description: |- - List of DNSNames that this solver will be used to solve. - If specified and a match is found, a dnsNames selector will take - precedence over a dnsZones selector. - If multiple solvers match with the same dnsNames value, the solver - with the most matching labels in matchLabels will be selected. - If neither has more matches, the solver defined earlier in the list - will be selected. - type: array - items: - type: string - dnsZones: - description: |- - List of DNSZones that this solver will be used to solve. - The most specific DNS zone match specified here will take precedence - over other DNS zone matches, so a solver specifying sys.example.com - will be selected over one specifying example.com for the domain - www.sys.example.com. - If multiple solvers match with the same dnsZones value, the solver - with the most matching labels in matchLabels will be selected. - If neither has more matches, the solver defined earlier in the list - will be selected. - type: array - items: - type: string - matchLabels: - description: |- - A label selector that is used to refine the set of certificate's that - this challenge solver will apply to. - type: object - additionalProperties: - type: string - ca: - description: |- - CA configures this issuer to sign certificates using a signing CA keypair - stored in a Secret resource. - This is used to build internal PKIs that are managed by cert-manager. - type: object - required: - - secretName - properties: - crlDistributionPoints: - description: |- - The CRL distribution points is an X.509 v3 certificate extension which identifies - the location of the CRL from which the revocation of this certificate can be checked. - If not set, certificates will be issued without distribution points set. - type: array - items: - type: string - issuingCertificateURLs: - description: |- - IssuingCertificateURLs is a list of URLs which this issuer should embed into certificates - it creates. See https://www.rfc-editor.org/rfc/rfc5280#section-4.2.2.1 for more details. - As an example, such a URL might be "http://ca.domain.com/ca.crt". - type: array - items: - type: string - ocspServers: - description: |- - The OCSP server list is an X.509 v3 extension that defines a list of - URLs of OCSP responders. The OCSP responders can be queried for the - revocation status of an issued certificate. If not set, the - certificate will be issued with no OCSP servers set. For example, an - OCSP server URL could be "http://ocsp.int-x3.letsencrypt.org". - type: array - items: - type: string - secretName: - description: |- - SecretName is the name of the secret used to sign Certificates issued - by this Issuer. - type: string - selfSigned: - description: |- - SelfSigned configures this issuer to 'self sign' certificates using the - private key used to create the CertificateRequest object. - type: object - properties: - crlDistributionPoints: - description: |- - The CRL distribution points is an X.509 v3 certificate extension which identifies - the location of the CRL from which the revocation of this certificate can be checked. - If not set certificate will be issued without CDP. Values are strings. - type: array - items: - type: string - vault: - description: |- - Vault configures this issuer to sign certificates using a HashiCorp Vault - PKI backend. - type: object - required: - - auth - - path - - server - properties: - auth: - description: Auth configures how cert-manager authenticates with the Vault server. - type: object - properties: - appRole: - description: |- - AppRole authenticates with Vault using the App Role auth mechanism, - with the role and secret stored in a Kubernetes Secret resource. - type: object - required: - - path - - roleId - - secretRef - properties: - path: - description: |- - Path where the App Role authentication backend is mounted in Vault, e.g: - "approle" - type: string - roleId: - description: |- - RoleID configured in the App Role authentication backend when setting - up the authentication backend in Vault. - type: string - secretRef: - description: |- - Reference to a key in a Secret that contains the App Role secret used - to authenticate with Vault. - The `key` field must be specified and denotes which entry within the Secret - resource is used as the app role secret. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientCertificate: - description: |- - ClientCertificate authenticates with Vault by presenting a client - certificate during the request's TLS handshake. - Works only when using HTTPS protocol. - type: object - properties: - mountPath: - description: |- - The Vault mountPath here is the mount path to use when authenticating with - Vault. For example, setting a value to `/v1/auth/foo`, will use the path - `/v1/auth/foo/login` to authenticate with Vault. If unspecified, the - default value "/v1/auth/cert" will be used. - type: string - name: - description: |- - Name of the certificate role to authenticate against. - If not set, matching any certificate role, if available. - type: string - secretName: - description: |- - Reference to Kubernetes Secret of type "kubernetes.io/tls" (hence containing - tls.crt and tls.key) used to authenticate to Vault using TLS client - authentication. - type: string - kubernetes: - description: |- - Kubernetes authenticates with Vault by passing the ServiceAccount - token stored in the named Secret resource to the Vault server. - type: object - required: - - role - properties: - mountPath: - description: |- - The Vault mountPath here is the mount path to use when authenticating with - Vault. For example, setting a value to `/v1/auth/foo`, will use the path - `/v1/auth/foo/login` to authenticate with Vault. If unspecified, the - default value "/v1/auth/kubernetes" will be used. - type: string - role: - description: |- - A required field containing the Vault Role to assume. A Role binds a - Kubernetes ServiceAccount with a set of Vault policies. - type: string - secretRef: - description: |- - The required Secret field containing a Kubernetes ServiceAccount JWT used - for authenticating with Vault. Use of 'ambient credentials' is not - supported. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - serviceAccountRef: - description: |- - A reference to a service account that will be used to request a bound - token (also known as "projected token"). Compared to using "secretRef", - using this field means that you don't rely on statically bound tokens. To - use this field, you must configure an RBAC rule to let cert-manager - request a token. - type: object - required: - - name - properties: - audiences: - description: |- - TokenAudiences is an optional list of extra audiences to include in the token passed to Vault. The default token - consisting of the issuer's namespace and name is always included. - type: array - items: - type: string - name: - description: Name of the ServiceAccount used to request a token. - type: string - tokenSecretRef: - description: TokenSecretRef authenticates with Vault by presenting a token. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - caBundle: - description: |- - Base64-encoded bundle of PEM CAs which will be used to validate the certificate - chain presented by Vault. Only used if using HTTPS to connect to Vault and - ignored for HTTP connections. - Mutually exclusive with CABundleSecretRef. - If neither CABundle nor CABundleSecretRef are defined, the certificate bundle in - the cert-manager controller container is used to validate the TLS connection. - type: string - format: byte - caBundleSecretRef: - description: |- - Reference to a Secret containing a bundle of PEM-encoded CAs to use when - verifying the certificate chain presented by Vault when using HTTPS. - Mutually exclusive with CABundle. - If neither CABundle nor CABundleSecretRef are defined, the certificate bundle in - the cert-manager controller container is used to validate the TLS connection. - If no key for the Secret is specified, cert-manager will default to 'ca.crt'. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientCertSecretRef: - description: |- - Reference to a Secret containing a PEM-encoded Client Certificate to use when the - Vault server requires mTLS. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientKeySecretRef: - description: |- - Reference to a Secret containing a PEM-encoded Client Private Key to use when the - Vault server requires mTLS. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - namespace: - description: |- - Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1" - More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces - type: string - path: - description: |- - Path is the mount path of the Vault PKI backend's `sign` endpoint, e.g: - "my_pki_mount/sign/my-role-name". - type: string - server: - description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' - type: string - venafi: - description: |- - Venafi configures this issuer to sign certificates using a Venafi TPP - or Venafi Cloud policy zone. - type: object - required: - - zone - properties: - cloud: - description: |- - Cloud specifies the Venafi cloud configuration settings. - Only one of TPP or Cloud may be specified. - type: object - required: - - apiTokenSecretRef - properties: - apiTokenSecretRef: - description: APITokenSecretRef is a secret key selector for the Venafi Cloud API token. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - url: - description: |- - URL is the base URL for Venafi Cloud. - Defaults to "https://api.venafi.cloud/v1". - type: string - tpp: - description: |- - TPP specifies Trust Protection Platform configuration settings. - Only one of TPP or Cloud may be specified. - type: object - required: - - credentialsRef - - url - properties: - caBundle: - description: |- - Base64-encoded bundle of PEM CAs which will be used to validate the certificate - chain presented by the TPP server. Only used if using HTTPS; ignored for HTTP. - If undefined, the certificate bundle in the cert-manager controller container - is used to validate the chain. - type: string - format: byte - caBundleSecretRef: - description: |- - Reference to a Secret containing a base64-encoded bundle of PEM CAs - which will be used to validate the certificate chain presented by the TPP server. - Only used if using HTTPS; ignored for HTTP. Mutually exclusive with CABundle. - If neither CABundle nor CABundleSecretRef is defined, the certificate bundle in - the cert-manager controller container is used to validate the TLS connection. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - credentialsRef: - description: |- - CredentialsRef is a reference to a Secret containing the Venafi TPP API credentials. - The secret must contain the key 'access-token' for the Access Token Authentication, - or two keys, 'username' and 'password' for the API Keys Authentication. - type: object - required: - - name - properties: - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - url: - description: |- - URL is the base URL for the vedsdk endpoint of the Venafi TPP instance, - for example: "https://tpp.example.com/vedsdk". - type: string - zone: - description: |- - Zone is the Venafi Policy Zone to use for this issuer. - All requests made to the Venafi platform will be restricted by the named - zone policy. - This field is required. - type: string - status: - description: Status of the ClusterIssuer. This is set and managed automatically. - type: object - properties: - acme: - description: |- - ACME specific status options. - This field should only be set if the Issuer is configured to use an ACME - server to issue certificates. - type: object - properties: - lastPrivateKeyHash: - description: |- - LastPrivateKeyHash is a hash of the private key associated with the latest - registered ACME account, in order to track changes made to registered account - associated with the Issuer - type: string - lastRegisteredEmail: - description: |- - LastRegisteredEmail is the email associated with the latest registered - ACME account, in order to track changes made to registered account - associated with the Issuer - type: string - uri: - description: |- - URI is the unique account identifier, which can also be used to retrieve - account details from the CA - type: string - conditions: - description: |- - List of status conditions to indicate the status of a CertificateRequest. - Known condition types are `Ready`. - type: array - items: - description: IssuerCondition contains condition information for an Issuer. - type: object - required: - - status - - type - properties: - lastTransitionTime: - description: |- - LastTransitionTime is the timestamp corresponding to the last status - change of this condition. - type: string - format: date-time - message: - description: |- - Message is a human readable description of the details of the last - transition, complementing reason. - type: string - observedGeneration: - description: |- - If set, this represents the .metadata.generation that the condition was - set based upon. - For instance, if .metadata.generation is currently 12, but the - .status.condition[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the Issuer. - type: integer - format: int64 - reason: - description: |- - Reason is a brief machine readable explanation for the condition's last - transition. - type: string - status: - description: Status of the condition, one of (`True`, `False`, `Unknown`). - type: string - enum: - - "True" - - "False" - - Unknown - type: - description: Type of the condition, known values are (`Ready`). - type: string - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - served: true - storage: true - -# END crd {{- end }} - ---- -# START crd {{- if or .Values.crds.enabled .Values.installCRDs }} -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: issuers.cert-manager.io - # START annotations {{- if .Values.crds.keep }} - annotations: - helm.sh/resource-policy: keep - # END annotations {{- end }} - labels: - app: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/name: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/instance: '{{ .Release.Name }}' - app.kubernetes.io/component: "crds" - # Generated labels {{- include "labels" . | nindent 4 }} -spec: - group: cert-manager.io - names: - kind: Issuer - listKind: IssuerList - plural: issuers - singular: issuer - categories: - - cert-manager - scope: Namespaced - versions: - - name: v1 - subresources: - status: {} - additionalPrinterColumns: - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - priority: 1 - type: string - - jsonPath: .metadata.creationTimestamp - description: CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC. - name: Age - type: date - schema: - openAPIV3Schema: - description: |- - An Issuer represents a certificate issuing authority which can be - referenced as part of `issuerRef` fields. - It is scoped to a single namespace and can therefore only be referenced by - resources within the same namespace. - type: object - required: - - spec - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: Desired state of the Issuer resource. - type: object - properties: - acme: - description: |- - ACME configures this issuer to communicate with a RFC8555 (ACME) server - to obtain signed x509 certificates. - type: object - required: - - privateKeySecretRef - - server - properties: - caBundle: - description: |- - Base64-encoded bundle of PEM CAs which can be used to validate the certificate - chain presented by the ACME server. - Mutually exclusive with SkipTLSVerify; prefer using CABundle to prevent various - kinds of security vulnerabilities. - If CABundle and SkipTLSVerify are unset, the system certificate bundle inside - the container is used to validate the TLS connection. - type: string - format: byte - disableAccountKeyGeneration: - description: |- - Enables or disables generating a new ACME account key. - If true, the Issuer resource will *not* request a new account but will expect - the account key to be supplied via an existing secret. - If false, the cert-manager system will generate a new ACME account key - for the Issuer. - Defaults to false. - type: boolean - email: - description: |- - Email is the email address to be associated with the ACME account. - This field is optional, but it is strongly recommended to be set. - It will be used to contact you in case of issues with your account or - certificates, including expiry notification emails. - This field may be updated after the account is initially registered. - type: string - enableDurationFeature: - description: |- - Enables requesting a Not After date on certificates that matches the - duration of the certificate. This is not supported by all ACME servers - like Let's Encrypt. If set to true when the ACME server does not support - it, it will create an error on the Order. - Defaults to false. - type: boolean - externalAccountBinding: - description: |- - ExternalAccountBinding is a reference to a CA external account of the ACME - server. - If set, upon registration cert-manager will attempt to associate the given - external account credentials with the registered ACME account. - type: object - required: - - keyID - - keySecretRef - properties: - keyAlgorithm: - description: |- - Deprecated: keyAlgorithm field exists for historical compatibility - reasons and should not be used. The algorithm is now hardcoded to HS256 - in golang/x/crypto/acme. - type: string - enum: - - HS256 - - HS384 - - HS512 - keyID: - description: keyID is the ID of the CA key that the External Account is bound to. - type: string - keySecretRef: - description: |- - keySecretRef is a Secret Key Selector referencing a data item in a Kubernetes - Secret which holds the symmetric MAC key of the External Account Binding. - The `key` is the index string that is paired with the key data in the - Secret and should not be confused with the key data itself, or indeed with - the External Account Binding keyID above. - The secret key stored in the Secret **must** be un-padded, base64 URL - encoded data. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - preferredChain: - description: |- - PreferredChain is the chain to use if the ACME server outputs multiple. - PreferredChain is no guarantee that this one gets delivered by the ACME - endpoint. - For example, for Let's Encrypt's DST crosssign you would use: - "DST Root CA X3" or "ISRG Root X1" for the newer Let's Encrypt root CA. - This value picks the first certificate bundle in the combined set of - ACME default and alternative chains that has a root-most certificate with - this value as its issuer's commonname. - type: string - maxLength: 64 - privateKeySecretRef: - description: |- - PrivateKey is the name of a Kubernetes Secret resource that will be used to - store the automatically generated ACME account private key. - Optionally, a `key` may be specified to select a specific entry within - the named Secret resource. - If `key` is not specified, a default of `tls.key` will be used. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - server: - description: |- - Server is the URL used to access the ACME server's 'directory' endpoint. - For example, for Let's Encrypt's staging endpoint, you would use: - "https://acme-staging-v02.api.letsencrypt.org/directory". - Only ACME v2 endpoints (i.e. RFC 8555) are supported. - type: string - skipTLSVerify: - description: |- - INSECURE: Enables or disables validation of the ACME server TLS certificate. - If true, requests to the ACME server will not have the TLS certificate chain - validated. - Mutually exclusive with CABundle; prefer using CABundle to prevent various - kinds of security vulnerabilities. - Only enable this option in development environments. - If CABundle and SkipTLSVerify are unset, the system certificate bundle inside - the container is used to validate the TLS connection. - Defaults to false. - type: boolean - solvers: - description: |- - Solvers is a list of challenge solvers that will be used to solve - ACME challenges for the matching domains. - Solver configurations must be provided in order to obtain certificates - from an ACME server. - For more information, see: https://cert-manager.io/docs/configuration/acme/ - type: array - items: - description: |- - An ACMEChallengeSolver describes how to solve ACME challenges for the issuer it is part of. - A selector may be provided to use different solving strategies for different DNS names. - Only one of HTTP01 or DNS01 must be provided. - type: object - properties: - dns01: - description: |- - Configures cert-manager to attempt to complete authorizations by - performing the DNS01 challenge flow. - type: object - properties: - acmeDNS: - description: |- - Use the 'ACME DNS' (https://github.com/joohoi/acme-dns) API to manage - DNS01 challenge records. - type: object - required: - - accountSecretRef - - host - properties: - accountSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - host: - type: string - akamai: - description: Use the Akamai DNS zone management API to manage DNS01 challenge records. - type: object - required: - - accessTokenSecretRef - - clientSecretSecretRef - - clientTokenSecretRef - - serviceConsumerDomain - properties: - accessTokenSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientSecretSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientTokenSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - serviceConsumerDomain: - type: string - azureDNS: - description: Use the Microsoft Azure DNS API to manage DNS01 challenge records. - type: object - required: - - resourceGroupName - - subscriptionID - properties: - clientID: - description: |- - Auth: Azure Service Principal: - The ClientID of the Azure Service Principal used to authenticate with Azure DNS. - If set, ClientSecret and TenantID must also be set. - type: string - clientSecretSecretRef: - description: |- - Auth: Azure Service Principal: - A reference to a Secret containing the password associated with the Service Principal. - If set, ClientID and TenantID must also be set. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - environment: - description: name of the Azure environment (default AzurePublicCloud) - type: string - enum: - - AzurePublicCloud - - AzureChinaCloud - - AzureGermanCloud - - AzureUSGovernmentCloud - hostedZoneName: - description: name of the DNS zone that should be used - type: string - managedIdentity: - description: |- - Auth: Azure Workload Identity or Azure Managed Service Identity: - Settings to enable Azure Workload Identity or Azure Managed Service Identity - If set, ClientID, ClientSecret and TenantID must not be set. - type: object - properties: - clientID: - description: client ID of the managed identity, can not be used at the same time as resourceID - type: string - resourceID: - description: |- - resource ID of the managed identity, can not be used at the same time as clientID - Cannot be used for Azure Managed Service Identity - type: string - resourceGroupName: - description: resource group the DNS zone is located in - type: string - subscriptionID: - description: ID of the Azure subscription - type: string - tenantID: - description: |- - Auth: Azure Service Principal: - The TenantID of the Azure Service Principal used to authenticate with Azure DNS. - If set, ClientID and ClientSecret must also be set. - type: string - cloudDNS: - description: Use the Google Cloud DNS API to manage DNS01 challenge records. - type: object - required: - - project - properties: - hostedZoneName: - description: |- - HostedZoneName is an optional field that tells cert-manager in which - Cloud DNS zone the challenge record has to be created. - If left empty cert-manager will automatically choose a zone. - type: string - project: - type: string - serviceAccountSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - cloudflare: - description: Use the Cloudflare API to manage DNS01 challenge records. - type: object - properties: - apiKeySecretRef: - description: |- - API key to use to authenticate with Cloudflare. - Note: using an API token to authenticate is now the recommended method - as it allows greater control of permissions. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - apiTokenSecretRef: - description: API token used to authenticate with Cloudflare. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - email: - description: Email of the account, only required when using API key based authentication. - type: string - cnameStrategy: - description: |- - CNAMEStrategy configures how the DNS01 provider should handle CNAME - records when found in DNS zones. - type: string - enum: - - None - - Follow - digitalocean: - description: Use the DigitalOcean DNS API to manage DNS01 challenge records. - type: object - required: - - tokenSecretRef - properties: - tokenSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - rfc2136: - description: |- - Use RFC2136 ("Dynamic Updates in the Domain Name System") (https://datatracker.ietf.org/doc/rfc2136/) - to manage DNS01 challenge records. - type: object - required: - - nameserver - properties: - nameserver: - description: |- - The IP address or hostname of an authoritative DNS server supporting - RFC2136 in the form host:port. If the host is an IPv6 address it must be - enclosed in square brackets (e.g [2001:db8::1]) ; port is optional. - This field is required. - type: string - tsigAlgorithm: - description: |- - The TSIG Algorithm configured in the DNS supporting RFC2136. Used only - when ``tsigSecretSecretRef`` and ``tsigKeyName`` are defined. - Supported values are (case-insensitive): ``HMACMD5`` (default), - ``HMACSHA1``, ``HMACSHA256`` or ``HMACSHA512``. - type: string - tsigKeyName: - description: |- - The TSIG Key name configured in the DNS. - If ``tsigSecretSecretRef`` is defined, this field is required. - type: string - tsigSecretSecretRef: - description: |- - The name of the secret containing the TSIG value. - If ``tsigKeyName`` is defined, this field is required. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - route53: - description: Use the AWS Route53 API to manage DNS01 challenge records. - type: object - properties: - accessKeyID: - description: |- - The AccessKeyID is used for authentication. - Cannot be set when SecretAccessKeyID is set. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, - see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials - type: string - accessKeyIDSecretRef: - description: |- - The SecretAccessKey is used for authentication. If set, pull the AWS - access key ID from a key within a Kubernetes Secret. - Cannot be set when AccessKeyID is set. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, - see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - auth: - description: Auth configures how cert-manager authenticates. - type: object - required: - - kubernetes - properties: - kubernetes: - description: |- - Kubernetes authenticates with Route53 using AssumeRoleWithWebIdentity - by passing a bound ServiceAccount token. - type: object - required: - - serviceAccountRef - properties: - serviceAccountRef: - description: |- - A reference to a service account that will be used to request a bound - token (also known as "projected token"). To use this field, you must - configure an RBAC rule to let cert-manager request a token. - type: object - required: - - name - properties: - audiences: - description: |- - TokenAudiences is an optional list of audiences to include in the - token passed to AWS. The default token consisting of the issuer's namespace - and name is always included. - If unset the audience defaults to `sts.amazonaws.com`. - type: array - items: - type: string - name: - description: Name of the ServiceAccount used to request a token. - type: string - hostedZoneID: - description: If set, the provider will manage only this zone in Route53 and will not do a lookup using the route53:ListHostedZonesByName api call. - type: string - region: - description: |- - Override the AWS region. - - Route53 is a global service and does not have regional endpoints but the - region specified here (or via environment variables) is used as a hint to - help compute the correct AWS credential scope and partition when it - connects to Route53. See: - - [Amazon Route 53 endpoints and quotas](https://docs.aws.amazon.com/general/latest/gr/r53.html) - - [Global services](https://docs.aws.amazon.com/whitepapers/latest/aws-fault-isolation-boundaries/global-services.html) - - If you omit this region field, cert-manager will use the region from - AWS_REGION and AWS_DEFAULT_REGION environment variables, if they are set - in the cert-manager controller Pod. - - The `region` field is not needed if you use [IAM Roles for Service Accounts (IRSA)](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html). - Instead an AWS_REGION environment variable is added to the cert-manager controller Pod by: - [Amazon EKS Pod Identity Webhook](https://github.com/aws/amazon-eks-pod-identity-webhook). - In this case this `region` field value is ignored. - - The `region` field is not needed if you use [EKS Pod Identities](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html). - Instead an AWS_REGION environment variable is added to the cert-manager controller Pod by: - [Amazon EKS Pod Identity Agent](https://github.com/aws/eks-pod-identity-agent), - In this case this `region` field value is ignored. - type: string - role: - description: |- - Role is a Role ARN which the Route53 provider will assume using either the explicit credentials AccessKeyID/SecretAccessKey - or the inferred credentials from environment variables, shared credentials file or AWS Instance metadata - type: string - secretAccessKeySecretRef: - description: |- - The SecretAccessKey is used for authentication. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, - see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - webhook: - description: |- - Configure an external webhook based DNS01 challenge solver to manage - DNS01 challenge records. - type: object - required: - - groupName - - solverName - properties: - config: - description: |- - Additional configuration that should be passed to the webhook apiserver - when challenges are processed. - This can contain arbitrary JSON data. - Secret values should not be specified in this stanza. - If secret values are needed (e.g. credentials for a DNS service), you - should use a SecretKeySelector to reference a Secret resource. - For details on the schema of this field, consult the webhook provider - implementation's documentation. - x-kubernetes-preserve-unknown-fields: true - groupName: - description: |- - The API group name that should be used when POSTing ChallengePayload - resources to the webhook apiserver. - This should be the same as the GroupName specified in the webhook - provider implementation. - type: string - solverName: - description: |- - The name of the solver to use, as defined in the webhook provider - implementation. - This will typically be the name of the provider, e.g. 'cloudflare'. - type: string - http01: - description: |- - Configures cert-manager to attempt to complete authorizations by - performing the HTTP01 challenge flow. - It is not possible to obtain certificates for wildcard domain names - (e.g. `*.example.com`) using the HTTP01 challenge mechanism. - type: object - properties: - gatewayHTTPRoute: - description: |- - The Gateway API is a sig-network community API that models service networking - in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will - create HTTPRoutes with the specified labels in the same namespace as the challenge. - This solver is experimental, and fields / behaviour may change in the future. - type: object - properties: - labels: - description: |- - Custom labels that will be applied to HTTPRoutes created by cert-manager - while solving HTTP-01 challenges. - type: object - additionalProperties: - type: string - parentRefs: - description: |- - When solving an HTTP-01 challenge, cert-manager creates an HTTPRoute. - cert-manager needs to know which parentRefs should be used when creating - the HTTPRoute. Usually, the parentRef references a Gateway. See: - https://gateway-api.sigs.k8s.io/api-types/httproute/#attaching-to-gateways - type: array - items: - description: |- - ParentReference identifies an API object (usually a Gateway) that can be considered - a parent of this resource (usually a route). There are two kinds of parent resources - with "Core" support: - - * Gateway (Gateway conformance profile) - * Service (Mesh conformance profile, ClusterIP Services only) - - This API may be extended in the future to support additional kinds of parent - resources. - - The API object must be valid in the cluster; the Group and Kind must - be registered in the cluster for this reference to be valid. - type: object - required: - - name - properties: - group: - description: |- - Group is the group of the referent. - When unspecified, "gateway.networking.k8s.io" is inferred. - To set the core API group (such as for a "Service" kind referent), - Group must be explicitly set to "" (empty string). - - Support: Core - type: string - default: gateway.networking.k8s.io - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - kind: - description: |- - Kind is kind of the referent. - - There are two kinds of parent resources with "Core" support: - - * Gateway (Gateway conformance profile) - * Service (Mesh conformance profile, ClusterIP Services only) - - Support for other resources is Implementation-Specific. - type: string - default: Gateway - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - name: - description: |- - Name is the name of the referent. - - Support: Core - type: string - maxLength: 253 - minLength: 1 - namespace: - description: |- - Namespace is the namespace of the referent. When unspecified, this refers - to the local namespace of the Route. - - Note that there are specific rules for ParentRefs which cross namespace - boundaries. Cross-namespace references are only valid if they are explicitly - allowed by something in the namespace they are referring to. For example: - Gateway has the AllowedRoutes field, and ReferenceGrant provides a - generic way to enable any other kind of cross-namespace reference. - - - ParentRefs from a Route to a Service in the same namespace are "producer" - routes, which apply default routing rules to inbound connections from - any namespace to the Service. - - ParentRefs from a Route to a Service in a different namespace are - "consumer" routes, and these routing rules are only applied to outbound - connections originating from the same namespace as the Route, for which - the intended destination of the connections are a Service targeted as a - ParentRef of the Route. - - - Support: Core - type: string - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - port: - description: |- - Port is the network port this Route targets. It can be interpreted - differently based on the type of parent resource. - - When the parent resource is a Gateway, this targets all listeners - listening on the specified port that also support this kind of Route(and - select this Route). It's not recommended to set `Port` unless the - networking behaviors specified in a Route must apply to a specific port - as opposed to a listener(s) whose port(s) may be changed. When both Port - and SectionName are specified, the name and port of the selected listener - must match both specified values. - - - When the parent resource is a Service, this targets a specific port in the - Service spec. When both Port (experimental) and SectionName are specified, - the name and port of the selected port must match both specified values. - - - Implementations MAY choose to support other parent resources. - Implementations supporting other types of parent resources MUST clearly - document how/if Port is interpreted. - - For the purpose of status, an attachment is considered successful as - long as the parent resource accepts it partially. For example, Gateway - listeners can restrict which Routes can attach to them by Route kind, - namespace, or hostname. If 1 of 2 Gateway listeners accept attachment - from the referencing Route, the Route MUST be considered successfully - attached. If no Gateway listeners accept attachment from this Route, - the Route MUST be considered detached from the Gateway. - - Support: Extended - type: integer - format: int32 - maximum: 65535 - minimum: 1 - sectionName: - description: |- - SectionName is the name of a section within the target resource. In the - following resources, SectionName is interpreted as the following: - - * Gateway: Listener name. When both Port (experimental) and SectionName - are specified, the name and port of the selected listener must match - both specified values. - * Service: Port name. When both Port (experimental) and SectionName - are specified, the name and port of the selected listener must match - both specified values. - - Implementations MAY choose to support attaching Routes to other resources. - If that is the case, they MUST clearly document how SectionName is - interpreted. - - When unspecified (empty string), this will reference the entire resource. - For the purpose of status, an attachment is considered successful if at - least one section in the parent resource accepts it. For example, Gateway - listeners can restrict which Routes can attach to them by Route kind, - namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from - the referencing Route, the Route MUST be considered successfully - attached. If no Gateway listeners accept attachment from this Route, the - Route MUST be considered detached from the Gateway. - - Support: Core - type: string - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - podTemplate: - description: |- - Optional pod template used to configure the ACME challenge solver pods - used for HTTP01 challenges. - type: object - properties: - metadata: - description: |- - ObjectMeta overrides for the pod used to solve HTTP01 challenges. - Only the 'labels' and 'annotations' fields may be set. - If labels or annotations overlap with in-built values, the values here - will override the in-built values. - type: object - properties: - annotations: - description: Annotations that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - labels: - description: Labels that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - spec: - description: |- - PodSpec defines overrides for the HTTP01 challenge solver pod. - Check ACMEChallengeSolverHTTP01IngressPodSpec to find out currently supported fields. - All other fields will be ignored. - type: object - properties: - affinity: - description: If specified, the pod's scheduling constraints - type: object - properties: - nodeAffinity: - description: Describes node affinity scheduling rules for the pod. - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node matches the corresponding matchExpressions; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: |- - An empty preferred scheduling term matches all objects with implicit weight 0 - (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). - type: object - required: - - preference - - weight - properties: - preference: - description: A node selector term, associated with the corresponding weight. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - weight: - description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to an update), the system - may or may not try to eventually evict the pod from its node. - type: object - required: - - nodeSelectorTerms - properties: - nodeSelectorTerms: - description: Required. A list of node selector terms. The terms are ORed. - type: array - items: - description: |- - A null or empty node selector term matches no objects. The requirements of - them are ANDed. - The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - podAffinity: - description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - podAntiAffinity: - description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the anti-affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the anti-affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the anti-affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - imagePullSecrets: - description: If specified, the pod's imagePullSecrets - type: array - items: - description: |- - LocalObjectReference contains enough information to let you locate the - referenced object inside the same namespace. - type: object - properties: - name: - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - default: "" - x-kubernetes-map-type: atomic - nodeSelector: - description: |- - NodeSelector is a selector which must be true for the pod to fit on a node. - Selector which must match a node's labels for the pod to be scheduled on that node. - More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ - type: object - additionalProperties: - type: string - priorityClassName: - description: If specified, the pod's priorityClassName. - type: string - securityContext: - description: If specified, the pod's security context - type: object - properties: - fsGroup: - description: |- - A special supplemental group that applies to all containers in a pod. - Some volume types allow the Kubelet to change the ownership of that volume - to be owned by the pod: - - 1. The owning GID will be the FSGroup - 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) - 3. The permission bits are OR'd with rw-rw---- - - If unset, the Kubelet will not modify the ownership and permissions of any volume. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - fsGroupChangePolicy: - description: |- - fsGroupChangePolicy defines behavior of changing ownership and permission of the volume - before being exposed inside Pod. This field will only apply to - volume types which support fsGroup based ownership(and permissions). - It will have no effect on ephemeral volume types such as: secret, configmaps - and emptydir. - Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. - Note that this field cannot be set when spec.os.name is windows. - type: string - runAsGroup: - description: |- - The GID to run the entrypoint of the container process. - Uses runtime default if unset. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - runAsNonRoot: - description: |- - Indicates that the container must run as a non-root user. - If true, the Kubelet will validate the image at runtime to ensure that it - does not run as UID 0 (root) and fail to start the container if it does. - If unset or false, no such validation will be performed. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: |- - The UID to run the entrypoint of the container process. - Defaults to user specified in image metadata if unspecified. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - seLinuxOptions: - description: |- - The SELinux context to be applied to all containers. - If unspecified, the container runtime will allocate a random SELinux context for each - container. May also be set in SecurityContext. If set in - both SecurityContext and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. - Note that this field cannot be set when spec.os.name is windows. - type: object - properties: - level: - description: Level is SELinux level label that applies to the container. - type: string - role: - description: Role is a SELinux role label that applies to the container. - type: string - type: - description: Type is a SELinux type label that applies to the container. - type: string - user: - description: User is a SELinux user label that applies to the container. - type: string - seccompProfile: - description: |- - The seccomp options to use by the containers in this pod. - Note that this field cannot be set when spec.os.name is windows. - type: object - required: - - type - properties: - localhostProfile: - description: |- - localhostProfile indicates a profile defined in a file on the node should be used. - The profile must be preconfigured on the node to work. - Must be a descending path, relative to the kubelet's configured seccomp profile location. - Must be set if type is "Localhost". Must NOT be set for any other type. - type: string - type: - description: |- - type indicates which kind of seccomp profile will be applied. - Valid options are: - - Localhost - a profile defined in a file on the node should be used. - RuntimeDefault - the container runtime default profile should be used. - Unconfined - no profile should be applied. - type: string - supplementalGroups: - description: |- - A list of groups applied to the first process run in each container, in addition - to the container's primary GID, the fsGroup (if specified), and group memberships - defined in the container image for the uid of the container process. If unspecified, - no additional groups are added to any container. Note that group memberships - defined in the container image for the uid of the container process are still effective, - even if they are not included in this list. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - type: integer - format: int64 - sysctls: - description: |- - Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported - sysctls (by the container runtime) might fail to launch. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - description: Sysctl defines a kernel parameter to be set - type: object - required: - - name - - value - properties: - name: - description: Name of a property to set - type: string - value: - description: Value of a property to set - type: string - serviceAccountName: - description: If specified, the pod's service account - type: string - tolerations: - description: If specified, the pod's tolerations. - type: array - items: - description: |- - The pod this Toleration is attached to tolerates any taint that matches - the triple using the matching operator . - type: object - properties: - effect: - description: |- - Effect indicates the taint effect to match. Empty means match all taint effects. - When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: |- - Key is the taint key that the toleration applies to. Empty means match all taint keys. - If the key is empty, operator must be Exists; this combination means to match all values and all keys. - type: string - operator: - description: |- - Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. - Exists is equivalent to wildcard for value, so that a pod can - tolerate all taints of a particular category. - type: string - tolerationSeconds: - description: |- - TolerationSeconds represents the period of time the toleration (which must be - of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, - it is not set, which means tolerate the taint forever (do not evict). Zero and - negative values will be treated as 0 (evict immediately) by the system. - type: integer - format: int64 - value: - description: |- - Value is the taint value the toleration matches to. - If the operator is Exists, the value should be empty, otherwise just a regular string. - type: string - serviceType: - description: |- - Optional service type for Kubernetes solver service. Supported values - are NodePort or ClusterIP. If unset, defaults to NodePort. - type: string - ingress: - description: |- - The ingress based HTTP01 challenge solver will solve challenges by - creating or modifying Ingress resources in order to route requests for - '/.well-known/acme-challenge/XYZ' to 'challenge solver' pods that are - provisioned by cert-manager for each Challenge to be completed. - type: object - properties: - class: - description: |- - This field configures the annotation `kubernetes.io/ingress.class` when - creating Ingress resources to solve ACME challenges that use this - challenge solver. Only one of `class`, `name` or `ingressClassName` may - be specified. - type: string - ingressClassName: - description: |- - This field configures the field `ingressClassName` on the created Ingress - resources used to solve ACME challenges that use this challenge solver. - This is the recommended way of configuring the ingress class. Only one of - `class`, `name` or `ingressClassName` may be specified. - type: string - ingressTemplate: - description: |- - Optional ingress template used to configure the ACME challenge solver - ingress used for HTTP01 challenges. - type: object - properties: - metadata: - description: |- - ObjectMeta overrides for the ingress used to solve HTTP01 challenges. - Only the 'labels' and 'annotations' fields may be set. - If labels or annotations overlap with in-built values, the values here - will override the in-built values. - type: object - properties: - annotations: - description: Annotations that should be added to the created ACME HTTP01 solver ingress. - type: object - additionalProperties: - type: string - labels: - description: Labels that should be added to the created ACME HTTP01 solver ingress. - type: object - additionalProperties: - type: string - name: - description: |- - The name of the ingress resource that should have ACME challenge solving - routes inserted into it in order to solve HTTP01 challenges. - This is typically used in conjunction with ingress controllers like - ingress-gce, which maintains a 1:1 mapping between external IPs and - ingress resources. Only one of `class`, `name` or `ingressClassName` may - be specified. - type: string - podTemplate: - description: |- - Optional pod template used to configure the ACME challenge solver pods - used for HTTP01 challenges. - type: object - properties: - metadata: - description: |- - ObjectMeta overrides for the pod used to solve HTTP01 challenges. - Only the 'labels' and 'annotations' fields may be set. - If labels or annotations overlap with in-built values, the values here - will override the in-built values. - type: object - properties: - annotations: - description: Annotations that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - labels: - description: Labels that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - spec: - description: |- - PodSpec defines overrides for the HTTP01 challenge solver pod. - Check ACMEChallengeSolverHTTP01IngressPodSpec to find out currently supported fields. - All other fields will be ignored. - type: object - properties: - affinity: - description: If specified, the pod's scheduling constraints - type: object - properties: - nodeAffinity: - description: Describes node affinity scheduling rules for the pod. - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node matches the corresponding matchExpressions; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: |- - An empty preferred scheduling term matches all objects with implicit weight 0 - (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). - type: object - required: - - preference - - weight - properties: - preference: - description: A node selector term, associated with the corresponding weight. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - weight: - description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to an update), the system - may or may not try to eventually evict the pod from its node. - type: object - required: - - nodeSelectorTerms - properties: - nodeSelectorTerms: - description: Required. A list of node selector terms. The terms are ORed. - type: array - items: - description: |- - A null or empty node selector term matches no objects. The requirements of - them are ANDed. - The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - podAffinity: - description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - podAntiAffinity: - description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the anti-affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the anti-affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the anti-affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - imagePullSecrets: - description: If specified, the pod's imagePullSecrets - type: array - items: - description: |- - LocalObjectReference contains enough information to let you locate the - referenced object inside the same namespace. - type: object - properties: - name: - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - default: "" - x-kubernetes-map-type: atomic - nodeSelector: - description: |- - NodeSelector is a selector which must be true for the pod to fit on a node. - Selector which must match a node's labels for the pod to be scheduled on that node. - More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ - type: object - additionalProperties: - type: string - priorityClassName: - description: If specified, the pod's priorityClassName. - type: string - securityContext: - description: If specified, the pod's security context - type: object - properties: - fsGroup: - description: |- - A special supplemental group that applies to all containers in a pod. - Some volume types allow the Kubelet to change the ownership of that volume - to be owned by the pod: - - 1. The owning GID will be the FSGroup - 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) - 3. The permission bits are OR'd with rw-rw---- - - If unset, the Kubelet will not modify the ownership and permissions of any volume. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - fsGroupChangePolicy: - description: |- - fsGroupChangePolicy defines behavior of changing ownership and permission of the volume - before being exposed inside Pod. This field will only apply to - volume types which support fsGroup based ownership(and permissions). - It will have no effect on ephemeral volume types such as: secret, configmaps - and emptydir. - Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. - Note that this field cannot be set when spec.os.name is windows. - type: string - runAsGroup: - description: |- - The GID to run the entrypoint of the container process. - Uses runtime default if unset. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - runAsNonRoot: - description: |- - Indicates that the container must run as a non-root user. - If true, the Kubelet will validate the image at runtime to ensure that it - does not run as UID 0 (root) and fail to start the container if it does. - If unset or false, no such validation will be performed. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: |- - The UID to run the entrypoint of the container process. - Defaults to user specified in image metadata if unspecified. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - seLinuxOptions: - description: |- - The SELinux context to be applied to all containers. - If unspecified, the container runtime will allocate a random SELinux context for each - container. May also be set in SecurityContext. If set in - both SecurityContext and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. - Note that this field cannot be set when spec.os.name is windows. - type: object - properties: - level: - description: Level is SELinux level label that applies to the container. - type: string - role: - description: Role is a SELinux role label that applies to the container. - type: string - type: - description: Type is a SELinux type label that applies to the container. - type: string - user: - description: User is a SELinux user label that applies to the container. - type: string - seccompProfile: - description: |- - The seccomp options to use by the containers in this pod. - Note that this field cannot be set when spec.os.name is windows. - type: object - required: - - type - properties: - localhostProfile: - description: |- - localhostProfile indicates a profile defined in a file on the node should be used. - The profile must be preconfigured on the node to work. - Must be a descending path, relative to the kubelet's configured seccomp profile location. - Must be set if type is "Localhost". Must NOT be set for any other type. - type: string - type: - description: |- - type indicates which kind of seccomp profile will be applied. - Valid options are: - - Localhost - a profile defined in a file on the node should be used. - RuntimeDefault - the container runtime default profile should be used. - Unconfined - no profile should be applied. - type: string - supplementalGroups: - description: |- - A list of groups applied to the first process run in each container, in addition - to the container's primary GID, the fsGroup (if specified), and group memberships - defined in the container image for the uid of the container process. If unspecified, - no additional groups are added to any container. Note that group memberships - defined in the container image for the uid of the container process are still effective, - even if they are not included in this list. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - type: integer - format: int64 - sysctls: - description: |- - Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported - sysctls (by the container runtime) might fail to launch. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - description: Sysctl defines a kernel parameter to be set - type: object - required: - - name - - value - properties: - name: - description: Name of a property to set - type: string - value: - description: Value of a property to set - type: string - serviceAccountName: - description: If specified, the pod's service account - type: string - tolerations: - description: If specified, the pod's tolerations. - type: array - items: - description: |- - The pod this Toleration is attached to tolerates any taint that matches - the triple using the matching operator . - type: object - properties: - effect: - description: |- - Effect indicates the taint effect to match. Empty means match all taint effects. - When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: |- - Key is the taint key that the toleration applies to. Empty means match all taint keys. - If the key is empty, operator must be Exists; this combination means to match all values and all keys. - type: string - operator: - description: |- - Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. - Exists is equivalent to wildcard for value, so that a pod can - tolerate all taints of a particular category. - type: string - tolerationSeconds: - description: |- - TolerationSeconds represents the period of time the toleration (which must be - of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, - it is not set, which means tolerate the taint forever (do not evict). Zero and - negative values will be treated as 0 (evict immediately) by the system. - type: integer - format: int64 - value: - description: |- - Value is the taint value the toleration matches to. - If the operator is Exists, the value should be empty, otherwise just a regular string. - type: string - serviceType: - description: |- - Optional service type for Kubernetes solver service. Supported values - are NodePort or ClusterIP. If unset, defaults to NodePort. - type: string - selector: - description: |- - Selector selects a set of DNSNames on the Certificate resource that - should be solved using this challenge solver. - If not specified, the solver will be treated as the 'default' solver - with the lowest priority, i.e. if any other solver has a more specific - match, it will be used instead. - type: object - properties: - dnsNames: - description: |- - List of DNSNames that this solver will be used to solve. - If specified and a match is found, a dnsNames selector will take - precedence over a dnsZones selector. - If multiple solvers match with the same dnsNames value, the solver - with the most matching labels in matchLabels will be selected. - If neither has more matches, the solver defined earlier in the list - will be selected. - type: array - items: - type: string - dnsZones: - description: |- - List of DNSZones that this solver will be used to solve. - The most specific DNS zone match specified here will take precedence - over other DNS zone matches, so a solver specifying sys.example.com - will be selected over one specifying example.com for the domain - www.sys.example.com. - If multiple solvers match with the same dnsZones value, the solver - with the most matching labels in matchLabels will be selected. - If neither has more matches, the solver defined earlier in the list - will be selected. - type: array - items: - type: string - matchLabels: - description: |- - A label selector that is used to refine the set of certificate's that - this challenge solver will apply to. - type: object - additionalProperties: - type: string - ca: - description: |- - CA configures this issuer to sign certificates using a signing CA keypair - stored in a Secret resource. - This is used to build internal PKIs that are managed by cert-manager. - type: object - required: - - secretName - properties: - crlDistributionPoints: - description: |- - The CRL distribution points is an X.509 v3 certificate extension which identifies - the location of the CRL from which the revocation of this certificate can be checked. - If not set, certificates will be issued without distribution points set. - type: array - items: - type: string - issuingCertificateURLs: - description: |- - IssuingCertificateURLs is a list of URLs which this issuer should embed into certificates - it creates. See https://www.rfc-editor.org/rfc/rfc5280#section-4.2.2.1 for more details. - As an example, such a URL might be "http://ca.domain.com/ca.crt". - type: array - items: - type: string - ocspServers: - description: |- - The OCSP server list is an X.509 v3 extension that defines a list of - URLs of OCSP responders. The OCSP responders can be queried for the - revocation status of an issued certificate. If not set, the - certificate will be issued with no OCSP servers set. For example, an - OCSP server URL could be "http://ocsp.int-x3.letsencrypt.org". - type: array - items: - type: string - secretName: - description: |- - SecretName is the name of the secret used to sign Certificates issued - by this Issuer. - type: string - selfSigned: - description: |- - SelfSigned configures this issuer to 'self sign' certificates using the - private key used to create the CertificateRequest object. - type: object - properties: - crlDistributionPoints: - description: |- - The CRL distribution points is an X.509 v3 certificate extension which identifies - the location of the CRL from which the revocation of this certificate can be checked. - If not set certificate will be issued without CDP. Values are strings. - type: array - items: - type: string - vault: - description: |- - Vault configures this issuer to sign certificates using a HashiCorp Vault - PKI backend. - type: object - required: - - auth - - path - - server - properties: - auth: - description: Auth configures how cert-manager authenticates with the Vault server. - type: object - properties: - appRole: - description: |- - AppRole authenticates with Vault using the App Role auth mechanism, - with the role and secret stored in a Kubernetes Secret resource. - type: object - required: - - path - - roleId - - secretRef - properties: - path: - description: |- - Path where the App Role authentication backend is mounted in Vault, e.g: - "approle" - type: string - roleId: - description: |- - RoleID configured in the App Role authentication backend when setting - up the authentication backend in Vault. - type: string - secretRef: - description: |- - Reference to a key in a Secret that contains the App Role secret used - to authenticate with Vault. - The `key` field must be specified and denotes which entry within the Secret - resource is used as the app role secret. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientCertificate: - description: |- - ClientCertificate authenticates with Vault by presenting a client - certificate during the request's TLS handshake. - Works only when using HTTPS protocol. - type: object - properties: - mountPath: - description: |- - The Vault mountPath here is the mount path to use when authenticating with - Vault. For example, setting a value to `/v1/auth/foo`, will use the path - `/v1/auth/foo/login` to authenticate with Vault. If unspecified, the - default value "/v1/auth/cert" will be used. - type: string - name: - description: |- - Name of the certificate role to authenticate against. - If not set, matching any certificate role, if available. - type: string - secretName: - description: |- - Reference to Kubernetes Secret of type "kubernetes.io/tls" (hence containing - tls.crt and tls.key) used to authenticate to Vault using TLS client - authentication. - type: string - kubernetes: - description: |- - Kubernetes authenticates with Vault by passing the ServiceAccount - token stored in the named Secret resource to the Vault server. - type: object - required: - - role - properties: - mountPath: - description: |- - The Vault mountPath here is the mount path to use when authenticating with - Vault. For example, setting a value to `/v1/auth/foo`, will use the path - `/v1/auth/foo/login` to authenticate with Vault. If unspecified, the - default value "/v1/auth/kubernetes" will be used. - type: string - role: - description: |- - A required field containing the Vault Role to assume. A Role binds a - Kubernetes ServiceAccount with a set of Vault policies. - type: string - secretRef: - description: |- - The required Secret field containing a Kubernetes ServiceAccount JWT used - for authenticating with Vault. Use of 'ambient credentials' is not - supported. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - serviceAccountRef: - description: |- - A reference to a service account that will be used to request a bound - token (also known as "projected token"). Compared to using "secretRef", - using this field means that you don't rely on statically bound tokens. To - use this field, you must configure an RBAC rule to let cert-manager - request a token. - type: object - required: - - name - properties: - audiences: - description: |- - TokenAudiences is an optional list of extra audiences to include in the token passed to Vault. The default token - consisting of the issuer's namespace and name is always included. - type: array - items: - type: string - name: - description: Name of the ServiceAccount used to request a token. - type: string - tokenSecretRef: - description: TokenSecretRef authenticates with Vault by presenting a token. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - caBundle: - description: |- - Base64-encoded bundle of PEM CAs which will be used to validate the certificate - chain presented by Vault. Only used if using HTTPS to connect to Vault and - ignored for HTTP connections. - Mutually exclusive with CABundleSecretRef. - If neither CABundle nor CABundleSecretRef are defined, the certificate bundle in - the cert-manager controller container is used to validate the TLS connection. - type: string - format: byte - caBundleSecretRef: - description: |- - Reference to a Secret containing a bundle of PEM-encoded CAs to use when - verifying the certificate chain presented by Vault when using HTTPS. - Mutually exclusive with CABundle. - If neither CABundle nor CABundleSecretRef are defined, the certificate bundle in - the cert-manager controller container is used to validate the TLS connection. - If no key for the Secret is specified, cert-manager will default to 'ca.crt'. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientCertSecretRef: - description: |- - Reference to a Secret containing a PEM-encoded Client Certificate to use when the - Vault server requires mTLS. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientKeySecretRef: - description: |- - Reference to a Secret containing a PEM-encoded Client Private Key to use when the - Vault server requires mTLS. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - namespace: - description: |- - Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1" - More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces - type: string - path: - description: |- - Path is the mount path of the Vault PKI backend's `sign` endpoint, e.g: - "my_pki_mount/sign/my-role-name". - type: string - server: - description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' - type: string - venafi: - description: |- - Venafi configures this issuer to sign certificates using a Venafi TPP - or Venafi Cloud policy zone. - type: object - required: - - zone - properties: - cloud: - description: |- - Cloud specifies the Venafi cloud configuration settings. - Only one of TPP or Cloud may be specified. - type: object - required: - - apiTokenSecretRef - properties: - apiTokenSecretRef: - description: APITokenSecretRef is a secret key selector for the Venafi Cloud API token. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - url: - description: |- - URL is the base URL for Venafi Cloud. - Defaults to "https://api.venafi.cloud/v1". - type: string - tpp: - description: |- - TPP specifies Trust Protection Platform configuration settings. - Only one of TPP or Cloud may be specified. - type: object - required: - - credentialsRef - - url - properties: - caBundle: - description: |- - Base64-encoded bundle of PEM CAs which will be used to validate the certificate - chain presented by the TPP server. Only used if using HTTPS; ignored for HTTP. - If undefined, the certificate bundle in the cert-manager controller container - is used to validate the chain. - type: string - format: byte - caBundleSecretRef: - description: |- - Reference to a Secret containing a base64-encoded bundle of PEM CAs - which will be used to validate the certificate chain presented by the TPP server. - Only used if using HTTPS; ignored for HTTP. Mutually exclusive with CABundle. - If neither CABundle nor CABundleSecretRef is defined, the certificate bundle in - the cert-manager controller container is used to validate the TLS connection. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - credentialsRef: - description: |- - CredentialsRef is a reference to a Secret containing the Venafi TPP API credentials. - The secret must contain the key 'access-token' for the Access Token Authentication, - or two keys, 'username' and 'password' for the API Keys Authentication. - type: object - required: - - name - properties: - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - url: - description: |- - URL is the base URL for the vedsdk endpoint of the Venafi TPP instance, - for example: "https://tpp.example.com/vedsdk". - type: string - zone: - description: |- - Zone is the Venafi Policy Zone to use for this issuer. - All requests made to the Venafi platform will be restricted by the named - zone policy. - This field is required. - type: string - status: - description: Status of the Issuer. This is set and managed automatically. - type: object - properties: - acme: - description: |- - ACME specific status options. - This field should only be set if the Issuer is configured to use an ACME - server to issue certificates. - type: object - properties: - lastPrivateKeyHash: - description: |- - LastPrivateKeyHash is a hash of the private key associated with the latest - registered ACME account, in order to track changes made to registered account - associated with the Issuer - type: string - lastRegisteredEmail: - description: |- - LastRegisteredEmail is the email associated with the latest registered - ACME account, in order to track changes made to registered account - associated with the Issuer - type: string - uri: - description: |- - URI is the unique account identifier, which can also be used to retrieve - account details from the CA - type: string - conditions: - description: |- - List of status conditions to indicate the status of a CertificateRequest. - Known condition types are `Ready`. - type: array - items: - description: IssuerCondition contains condition information for an Issuer. - type: object - required: - - status - - type - properties: - lastTransitionTime: - description: |- - LastTransitionTime is the timestamp corresponding to the last status - change of this condition. - type: string - format: date-time - message: - description: |- - Message is a human readable description of the details of the last - transition, complementing reason. - type: string - observedGeneration: - description: |- - If set, this represents the .metadata.generation that the condition was - set based upon. - For instance, if .metadata.generation is currently 12, but the - .status.condition[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the Issuer. - type: integer - format: int64 - reason: - description: |- - Reason is a brief machine readable explanation for the condition's last - transition. - type: string - status: - description: Status of the condition, one of (`True`, `False`, `Unknown`). - type: string - enum: - - "True" - - "False" - - Unknown - type: - description: Type of the condition, known values are (`Ready`). - type: string - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - served: true - storage: true - -# END crd {{- end }} - ---- -# START crd {{- if or .Values.crds.enabled .Values.installCRDs }} -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: orders.acme.cert-manager.io - # START annotations {{- if .Values.crds.keep }} - annotations: - helm.sh/resource-policy: keep - # END annotations {{- end }} - labels: - app: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/name: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/instance: '{{ .Release.Name }}' - app.kubernetes.io/component: "crds" - # Generated labels {{- include "labels" . | nindent 4 }} -spec: - group: acme.cert-manager.io - names: - kind: Order - listKind: OrderList - plural: orders - singular: order - categories: - - cert-manager - - cert-manager-acme - scope: Namespaced - versions: - - name: v1 - subresources: - status: {} - additionalPrinterColumns: - - jsonPath: .status.state - name: State - type: string - - jsonPath: .spec.issuerRef.name - name: Issuer - priority: 1 - type: string - - jsonPath: .status.reason - name: Reason - priority: 1 - type: string - - jsonPath: .metadata.creationTimestamp - description: CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC. - name: Age - type: date - schema: - openAPIV3Schema: - description: Order is a type to represent an Order with an ACME server - type: object - required: - - metadata - - spec - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - type: object - required: - - issuerRef - - request - properties: - commonName: - description: |- - CommonName is the common name as specified on the DER encoded CSR. - If specified, this value must also be present in `dnsNames` or `ipAddresses`. - This field must match the corresponding field on the DER encoded CSR. - type: string - dnsNames: - description: |- - DNSNames is a list of DNS names that should be included as part of the Order - validation process. - This field must match the corresponding field on the DER encoded CSR. - type: array - items: - type: string - duration: - description: |- - Duration is the duration for the not after date for the requested certificate. - this is set on order creation as pe the ACME spec. - type: string - ipAddresses: - description: |- - IPAddresses is a list of IP addresses that should be included as part of the Order - validation process. - This field must match the corresponding field on the DER encoded CSR. - type: array - items: - type: string - issuerRef: - description: |- - IssuerRef references a properly configured ACME-type Issuer which should - be used to create this Order. - If the Issuer does not exist, processing will be retried. - If the Issuer is not an 'ACME' Issuer, an error will be returned and the - Order will be marked as failed. - type: object - required: - - name - properties: - group: - description: Group of the resource being referred to. - type: string - kind: - description: Kind of the resource being referred to. - type: string - name: - description: Name of the resource being referred to. - type: string - request: - description: |- - Certificate signing request bytes in DER encoding. - This will be used when finalizing the order. - This field must be set on the order. - type: string - format: byte - status: - type: object - properties: - authorizations: - description: |- - Authorizations contains data returned from the ACME server on what - authorizations must be completed in order to validate the DNS names - specified on the Order. - type: array - items: - description: |- - ACMEAuthorization contains data returned from the ACME server on an - authorization that must be completed in order validate a DNS name on an ACME - Order resource. - type: object - required: - - url - properties: - challenges: - description: |- - Challenges specifies the challenge types offered by the ACME server. - One of these challenge types will be selected when validating the DNS - name and an appropriate Challenge resource will be created to perform - the ACME challenge process. - type: array - items: - description: |- - Challenge specifies a challenge offered by the ACME server for an Order. - An appropriate Challenge resource can be created to perform the ACME - challenge process. - type: object - required: - - token - - type - - url - properties: - token: - description: |- - Token is the token that must be presented for this challenge. - This is used to compute the 'key' that must also be presented. - type: string - type: - description: |- - Type is the type of challenge being offered, e.g. 'http-01', 'dns-01', - 'tls-sni-01', etc. - This is the raw value retrieved from the ACME server. - Only 'http-01' and 'dns-01' are supported by cert-manager, other values - will be ignored. - type: string - url: - description: |- - URL is the URL of this challenge. It can be used to retrieve additional - metadata about the Challenge from the ACME server. - type: string - identifier: - description: Identifier is the DNS name to be validated as part of this authorization - type: string - initialState: - description: |- - InitialState is the initial state of the ACME authorization when first - fetched from the ACME server. - If an Authorization is already 'valid', the Order controller will not - create a Challenge resource for the authorization. This will occur when - working with an ACME server that enables 'authz reuse' (such as Let's - Encrypt's production endpoint). - If not set and 'identifier' is set, the state is assumed to be pending - and a Challenge will be created. - type: string - enum: - - valid - - ready - - pending - - processing - - invalid - - expired - - errored - url: - description: URL is the URL of the Authorization that must be completed - type: string - wildcard: - description: |- - Wildcard will be true if this authorization is for a wildcard DNS name. - If this is true, the identifier will be the *non-wildcard* version of - the DNS name. - For example, if '*.example.com' is the DNS name being validated, this - field will be 'true' and the 'identifier' field will be 'example.com'. - type: boolean - certificate: - description: |- - Certificate is a copy of the PEM encoded certificate for this Order. - This field will be populated after the order has been successfully - finalized with the ACME server, and the order has transitioned to the - 'valid' state. - type: string - format: byte - failureTime: - description: |- - FailureTime stores the time that this order failed. - This is used to influence garbage collection and back-off. - type: string - format: date-time - finalizeURL: - description: |- - FinalizeURL of the Order. - This is used to obtain certificates for this order once it has been completed. - type: string - reason: - description: |- - Reason optionally provides more information about a why the order is in - the current state. - type: string - state: - description: |- - State contains the current state of this Order resource. - States 'success' and 'expired' are 'final' - type: string - enum: - - valid - - ready - - pending - - processing - - invalid - - expired - - errored - url: - description: |- - URL of the Order. - This will initially be empty when the resource is first created. - The Order controller will populate this field when the Order is first processed. - This field will be immutable after it is initially set. - type: string - served: true - storage: true - -# END crd {{- end }} diff --git a/packages/system/cert-manager-crds/charts/cert-manager/values.schema.json b/packages/system/cert-manager-crds/charts/cert-manager/values.schema.json deleted file mode 100644 index d04da90c..00000000 --- a/packages/system/cert-manager-crds/charts/cert-manager/values.schema.json +++ /dev/null @@ -1,2135 +0,0 @@ -{ - "$defs": { - "helm-values": { - "additionalProperties": false, - "properties": { - "acmesolver": { - "$ref": "#/$defs/helm-values.acmesolver" - }, - "affinity": { - "$ref": "#/$defs/helm-values.affinity" - }, - "approveSignerNames": { - "$ref": "#/$defs/helm-values.approveSignerNames" - }, - "automountServiceAccountToken": { - "$ref": "#/$defs/helm-values.automountServiceAccountToken" - }, - "cainjector": { - "$ref": "#/$defs/helm-values.cainjector" - }, - "clusterResourceNamespace": { - "$ref": "#/$defs/helm-values.clusterResourceNamespace" - }, - "config": { - "$ref": "#/$defs/helm-values.config" - }, - "containerSecurityContext": { - "$ref": "#/$defs/helm-values.containerSecurityContext" - }, - "crds": { - "$ref": "#/$defs/helm-values.crds" - }, - "creator": { - "$ref": "#/$defs/helm-values.creator" - }, - "deploymentAnnotations": { - "$ref": "#/$defs/helm-values.deploymentAnnotations" - }, - "disableAutoApproval": { - "$ref": "#/$defs/helm-values.disableAutoApproval" - }, - "dns01RecursiveNameservers": { - "$ref": "#/$defs/helm-values.dns01RecursiveNameservers" - }, - "dns01RecursiveNameserversOnly": { - "$ref": "#/$defs/helm-values.dns01RecursiveNameserversOnly" - }, - "enableCertificateOwnerRef": { - "$ref": "#/$defs/helm-values.enableCertificateOwnerRef" - }, - "enableServiceLinks": { - "$ref": "#/$defs/helm-values.enableServiceLinks" - }, - "enabled": { - "$ref": "#/$defs/helm-values.enabled" - }, - "extraArgs": { - "$ref": "#/$defs/helm-values.extraArgs" - }, - "extraEnv": { - "$ref": "#/$defs/helm-values.extraEnv" - }, - "extraObjects": { - "$ref": "#/$defs/helm-values.extraObjects" - }, - "featureGates": { - "$ref": "#/$defs/helm-values.featureGates" - }, - "fullnameOverride": { - "$ref": "#/$defs/helm-values.fullnameOverride" - }, - "global": { - "$ref": "#/$defs/helm-values.global" - }, - "hostAliases": { - "$ref": "#/$defs/helm-values.hostAliases" - }, - "http_proxy": { - "$ref": "#/$defs/helm-values.http_proxy" - }, - "https_proxy": { - "$ref": "#/$defs/helm-values.https_proxy" - }, - "image": { - "$ref": "#/$defs/helm-values.image" - }, - "ingressShim": { - "$ref": "#/$defs/helm-values.ingressShim" - }, - "installCRDs": { - "$ref": "#/$defs/helm-values.installCRDs" - }, - "livenessProbe": { - "$ref": "#/$defs/helm-values.livenessProbe" - }, - "maxConcurrentChallenges": { - "$ref": "#/$defs/helm-values.maxConcurrentChallenges" - }, - "nameOverride": { - "$ref": "#/$defs/helm-values.nameOverride" - }, - "namespace": { - "$ref": "#/$defs/helm-values.namespace" - }, - "no_proxy": { - "$ref": "#/$defs/helm-values.no_proxy" - }, - "nodeSelector": { - "$ref": "#/$defs/helm-values.nodeSelector" - }, - "podAnnotations": { - "$ref": "#/$defs/helm-values.podAnnotations" - }, - "podDisruptionBudget": { - "$ref": "#/$defs/helm-values.podDisruptionBudget" - }, - "podDnsConfig": { - "$ref": "#/$defs/helm-values.podDnsConfig" - }, - "podDnsPolicy": { - "$ref": "#/$defs/helm-values.podDnsPolicy" - }, - "podLabels": { - "$ref": "#/$defs/helm-values.podLabels" - }, - "prometheus": { - "$ref": "#/$defs/helm-values.prometheus" - }, - "replicaCount": { - "$ref": "#/$defs/helm-values.replicaCount" - }, - "resources": { - "$ref": "#/$defs/helm-values.resources" - }, - "securityContext": { - "$ref": "#/$defs/helm-values.securityContext" - }, - "serviceAccount": { - "$ref": "#/$defs/helm-values.serviceAccount" - }, - "serviceAnnotations": { - "$ref": "#/$defs/helm-values.serviceAnnotations" - }, - "serviceIPFamilies": { - "$ref": "#/$defs/helm-values.serviceIPFamilies" - }, - "serviceIPFamilyPolicy": { - "$ref": "#/$defs/helm-values.serviceIPFamilyPolicy" - }, - "serviceLabels": { - "$ref": "#/$defs/helm-values.serviceLabels" - }, - "startupapicheck": { - "$ref": "#/$defs/helm-values.startupapicheck" - }, - "strategy": { - "$ref": "#/$defs/helm-values.strategy" - }, - "tolerations": { - "$ref": "#/$defs/helm-values.tolerations" - }, - "topologySpreadConstraints": { - "$ref": "#/$defs/helm-values.topologySpreadConstraints" - }, - "volumeMounts": { - "$ref": "#/$defs/helm-values.volumeMounts" - }, - "volumes": { - "$ref": "#/$defs/helm-values.volumes" - }, - "webhook": { - "$ref": "#/$defs/helm-values.webhook" - } - }, - "type": "object" - }, - "helm-values.acmesolver": { - "additionalProperties": false, - "properties": { - "image": { - "$ref": "#/$defs/helm-values.acmesolver.image" - } - }, - "type": "object" - }, - "helm-values.acmesolver.image": { - "additionalProperties": false, - "properties": { - "digest": { - "$ref": "#/$defs/helm-values.acmesolver.image.digest" - }, - "pullPolicy": { - "$ref": "#/$defs/helm-values.acmesolver.image.pullPolicy" - }, - "registry": { - "$ref": "#/$defs/helm-values.acmesolver.image.registry" - }, - "repository": { - "$ref": "#/$defs/helm-values.acmesolver.image.repository" - }, - "tag": { - "$ref": "#/$defs/helm-values.acmesolver.image.tag" - } - }, - "type": "object" - }, - "helm-values.acmesolver.image.digest": { - "description": "Setting a digest will override any tag.", - "type": "string" - }, - "helm-values.acmesolver.image.pullPolicy": { - "default": "IfNotPresent", - "description": "Kubernetes imagePullPolicy on Deployment.", - "type": "string" - }, - "helm-values.acmesolver.image.registry": { - "description": "The container registry to pull the acmesolver image from.", - "type": "string" - }, - "helm-values.acmesolver.image.repository": { - "default": "quay.io/jetstack/cert-manager-acmesolver", - "description": "The container image for the cert-manager acmesolver.", - "type": "string" - }, - "helm-values.acmesolver.image.tag": { - "description": "Override the image tag to deploy by setting this variable. If no value is set, the chart's appVersion is used.", - "type": "string" - }, - "helm-values.affinity": { - "default": {}, - "description": "A Kubernetes Affinity, if required. For more information, see [Affinity v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#affinity-v1-core).\n\nFor example:\naffinity:\n nodeAffinity:\n requiredDuringSchedulingIgnoredDuringExecution:\n nodeSelectorTerms:\n - matchExpressions:\n - key: foo.bar.com/role\n operator: In\n values:\n - master", - "type": "object" - }, - "helm-values.approveSignerNames": { - "default": [ - "issuers.cert-manager.io/*", - "clusterissuers.cert-manager.io/*" - ], - "description": "List of signer names that cert-manager will approve by default. CertificateRequests referencing these signer names will be auto-approved by cert-manager. Defaults to just approving the cert-manager.io Issuer and ClusterIssuer issuers. When set to an empty array, ALL issuers will be auto-approved by cert-manager. To disable the auto-approval, because eg. you are using approver-policy, you can enable 'disableAutoApproval'.\nref: https://cert-manager.io/docs/concepts/certificaterequest/#approval", - "items": {}, - "type": "array" - }, - "helm-values.automountServiceAccountToken": { - "description": "Automounting API credentials for a particular pod.", - "type": "boolean" - }, - "helm-values.cainjector": { - "additionalProperties": false, - "properties": { - "affinity": { - "$ref": "#/$defs/helm-values.cainjector.affinity" - }, - "automountServiceAccountToken": { - "$ref": "#/$defs/helm-values.cainjector.automountServiceAccountToken" - }, - "config": { - "$ref": "#/$defs/helm-values.cainjector.config" - }, - "containerSecurityContext": { - "$ref": "#/$defs/helm-values.cainjector.containerSecurityContext" - }, - "deploymentAnnotations": { - "$ref": "#/$defs/helm-values.cainjector.deploymentAnnotations" - }, - "enableServiceLinks": { - "$ref": "#/$defs/helm-values.cainjector.enableServiceLinks" - }, - "enabled": { - "$ref": "#/$defs/helm-values.cainjector.enabled" - }, - "extraArgs": { - "$ref": "#/$defs/helm-values.cainjector.extraArgs" - }, - "extraEnv": { - "$ref": "#/$defs/helm-values.cainjector.extraEnv" - }, - "featureGates": { - "$ref": "#/$defs/helm-values.cainjector.featureGates" - }, - "image": { - "$ref": "#/$defs/helm-values.cainjector.image" - }, - "nodeSelector": { - "$ref": "#/$defs/helm-values.cainjector.nodeSelector" - }, - "podAnnotations": { - "$ref": "#/$defs/helm-values.cainjector.podAnnotations" - }, - "podDisruptionBudget": { - "$ref": "#/$defs/helm-values.cainjector.podDisruptionBudget" - }, - "podLabels": { - "$ref": "#/$defs/helm-values.cainjector.podLabels" - }, - "replicaCount": { - "$ref": "#/$defs/helm-values.cainjector.replicaCount" - }, - "resources": { - "$ref": "#/$defs/helm-values.cainjector.resources" - }, - "securityContext": { - "$ref": "#/$defs/helm-values.cainjector.securityContext" - }, - "serviceAccount": { - "$ref": "#/$defs/helm-values.cainjector.serviceAccount" - }, - "serviceAnnotations": { - "$ref": "#/$defs/helm-values.cainjector.serviceAnnotations" - }, - "serviceLabels": { - "$ref": "#/$defs/helm-values.cainjector.serviceLabels" - }, - "strategy": { - "$ref": "#/$defs/helm-values.cainjector.strategy" - }, - "tolerations": { - "$ref": "#/$defs/helm-values.cainjector.tolerations" - }, - "topologySpreadConstraints": { - "$ref": "#/$defs/helm-values.cainjector.topologySpreadConstraints" - }, - "volumeMounts": { - "$ref": "#/$defs/helm-values.cainjector.volumeMounts" - }, - "volumes": { - "$ref": "#/$defs/helm-values.cainjector.volumes" - } - }, - "type": "object" - }, - "helm-values.cainjector.affinity": { - "default": {}, - "description": "A Kubernetes Affinity, if required. For more information, see [Affinity v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#affinity-v1-core).\n\nFor example:\naffinity:\n nodeAffinity:\n requiredDuringSchedulingIgnoredDuringExecution:\n nodeSelectorTerms:\n - matchExpressions:\n - key: foo.bar.com/role\n operator: In\n values:\n - master", - "type": "object" - }, - "helm-values.cainjector.automountServiceAccountToken": { - "description": "Automounting API credentials for a particular pod.", - "type": "boolean" - }, - "helm-values.cainjector.config": { - "default": {}, - "description": "This is used to configure options for the cainjector pod. It allows setting options that are usually provided via flags.\n\nIf `apiVersion` and `kind` are unspecified they default to the current latest version (currently `cainjector.config.cert-manager.io/v1alpha1`). You can pin the version by specifying the `apiVersion` yourself.\n\nFor example:\napiVersion: cainjector.config.cert-manager.io/v1alpha1\nkind: CAInjectorConfiguration\nlogging:\n verbosity: 2\n format: text\nleaderElectionConfig:\n namespace: kube-system\n# Configure the metrics server for TLS\n# See https://cert-manager.io/docs/devops-tips/prometheus-metrics/#tls\nmetricsTLSConfig:\n dynamic:\n secretNamespace: \"cert-manager\"\n secretName: \"cert-manager-metrics-ca\"\n dnsNames:\n - cert-manager-metrics", - "type": "object" - }, - "helm-values.cainjector.containerSecurityContext": { - "default": { - "allowPrivilegeEscalation": false, - "capabilities": { - "drop": [ - "ALL" - ] - }, - "readOnlyRootFilesystem": true - }, - "description": "Container Security Context to be set on the cainjector component container. For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/).", - "type": "object" - }, - "helm-values.cainjector.deploymentAnnotations": { - "description": "Optional additional annotations to add to the cainjector Deployment.", - "type": "object" - }, - "helm-values.cainjector.enableServiceLinks": { - "default": false, - "description": "enableServiceLinks indicates whether information about services should be injected into the pod's environment variables, matching the syntax of Docker links.", - "type": "boolean" - }, - "helm-values.cainjector.enabled": { - "default": true, - "description": "Create the CA Injector deployment", - "type": "boolean" - }, - "helm-values.cainjector.extraArgs": { - "default": [], - "description": "Additional command line flags to pass to cert-manager cainjector binary. To see all available flags run `docker run quay.io/jetstack/cert-manager-cainjector: --help`.", - "items": {}, - "type": "array" - }, - "helm-values.cainjector.extraEnv": { - "default": [], - "description": "Additional environment variables to pass to cert-manager cainjector binary.\nFor example:\nextraEnv:\n- name: SOME_VAR\n value: 'some value'", - "items": {}, - "type": "array" - }, - "helm-values.cainjector.featureGates": { - "default": "", - "description": "Comma separated list of feature gates that should be enabled on the cainjector pod.", - "type": "string" - }, - "helm-values.cainjector.image": { - "additionalProperties": false, - "properties": { - "digest": { - "$ref": "#/$defs/helm-values.cainjector.image.digest" - }, - "pullPolicy": { - "$ref": "#/$defs/helm-values.cainjector.image.pullPolicy" - }, - "registry": { - "$ref": "#/$defs/helm-values.cainjector.image.registry" - }, - "repository": { - "$ref": "#/$defs/helm-values.cainjector.image.repository" - }, - "tag": { - "$ref": "#/$defs/helm-values.cainjector.image.tag" - } - }, - "type": "object" - }, - "helm-values.cainjector.image.digest": { - "description": "Setting a digest will override any tag.", - "type": "string" - }, - "helm-values.cainjector.image.pullPolicy": { - "default": "IfNotPresent", - "description": "Kubernetes imagePullPolicy on Deployment.", - "type": "string" - }, - "helm-values.cainjector.image.registry": { - "description": "The container registry to pull the cainjector image from.", - "type": "string" - }, - "helm-values.cainjector.image.repository": { - "default": "quay.io/jetstack/cert-manager-cainjector", - "description": "The container image for the cert-manager cainjector", - "type": "string" - }, - "helm-values.cainjector.image.tag": { - "description": "Override the image tag to deploy by setting this variable. If no value is set, the chart's appVersion will be used.", - "type": "string" - }, - "helm-values.cainjector.nodeSelector": { - "default": { - "kubernetes.io/os": "linux" - }, - "description": "The nodeSelector on Pods tells Kubernetes to schedule Pods on the nodes with matching labels. For more information, see [Assigning Pods to Nodes](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/).\n\nThis default ensures that Pods are only scheduled to Linux nodes. It prevents Pods being scheduled to Windows nodes in a mixed OS cluster.", - "type": "object" - }, - "helm-values.cainjector.podAnnotations": { - "description": "Optional additional annotations to add to the cainjector Pods.", - "type": "object" - }, - "helm-values.cainjector.podDisruptionBudget": { - "additionalProperties": false, - "properties": { - "enabled": { - "$ref": "#/$defs/helm-values.cainjector.podDisruptionBudget.enabled" - }, - "maxUnavailable": { - "$ref": "#/$defs/helm-values.cainjector.podDisruptionBudget.maxUnavailable" - }, - "minAvailable": { - "$ref": "#/$defs/helm-values.cainjector.podDisruptionBudget.minAvailable" - } - }, - "type": "object" - }, - "helm-values.cainjector.podDisruptionBudget.enabled": { - "default": false, - "description": "Enable or disable the PodDisruptionBudget resource.\n\nThis prevents downtime during voluntary disruptions such as during a Node upgrade. For example, the PodDisruptionBudget will block `kubectl drain` if it is used on the Node where the only remaining cert-manager\nPod is currently running.", - "type": "boolean" - }, - "helm-values.cainjector.podDisruptionBudget.maxUnavailable": { - "description": "`maxUnavailable` configures the maximum unavailable pods for disruptions. It can either be set to\nan integer (e.g. 1) or a percentage value (e.g. 25%).\nCannot be used if `minAvailable` is set." - }, - "helm-values.cainjector.podDisruptionBudget.minAvailable": { - "description": "`minAvailable` configures the minimum available pods for disruptions. It can either be set to\nan integer (e.g. 1) or a percentage value (e.g. 25%).\nCannot be used if `maxUnavailable` is set." - }, - "helm-values.cainjector.podLabels": { - "default": {}, - "description": "Optional additional labels to add to the CA Injector Pods.", - "type": "object" - }, - "helm-values.cainjector.replicaCount": { - "default": 1, - "description": "The number of replicas of the cert-manager cainjector to run.\n\nThe default is 1, but in production set this to 2 or 3 to provide high availability.\n\nIf `replicas > 1`, consider setting `cainjector.podDisruptionBudget.enabled=true`.\n\nNote that cert-manager uses leader election to ensure that there can only be a single instance active at a time.", - "type": "number" - }, - "helm-values.cainjector.resources": { - "default": {}, - "description": "Resources to provide to the cert-manager cainjector pod.\n\nFor example:\nrequests:\n cpu: 10m\n memory: 32Mi\nFor more information, see [Resource Management for Pods and Containers](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/).", - "type": "object" - }, - "helm-values.cainjector.securityContext": { - "default": { - "runAsNonRoot": true, - "seccompProfile": { - "type": "RuntimeDefault" - } - }, - "description": "Pod Security Context to be set on the cainjector component Pod. For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/).", - "type": "object" - }, - "helm-values.cainjector.serviceAccount": { - "additionalProperties": false, - "properties": { - "annotations": { - "$ref": "#/$defs/helm-values.cainjector.serviceAccount.annotations" - }, - "automountServiceAccountToken": { - "$ref": "#/$defs/helm-values.cainjector.serviceAccount.automountServiceAccountToken" - }, - "create": { - "$ref": "#/$defs/helm-values.cainjector.serviceAccount.create" - }, - "labels": { - "$ref": "#/$defs/helm-values.cainjector.serviceAccount.labels" - }, - "name": { - "$ref": "#/$defs/helm-values.cainjector.serviceAccount.name" - } - }, - "type": "object" - }, - "helm-values.cainjector.serviceAccount.annotations": { - "description": "Optional additional annotations to add to the cainjector's Service Account.", - "type": "object" - }, - "helm-values.cainjector.serviceAccount.automountServiceAccountToken": { - "default": true, - "description": "Automount API credentials for a Service Account.", - "type": "boolean" - }, - "helm-values.cainjector.serviceAccount.create": { - "default": true, - "description": "Specifies whether a service account should be created.", - "type": "boolean" - }, - "helm-values.cainjector.serviceAccount.labels": { - "description": "Optional additional labels to add to the cainjector's Service Account.", - "type": "object" - }, - "helm-values.cainjector.serviceAccount.name": { - "description": "The name of the service account to use.\nIf not set and create is true, a name is generated using the fullname template", - "type": "string" - }, - "helm-values.cainjector.serviceAnnotations": { - "description": "Optional additional annotations to add to the cainjector metrics Service.", - "type": "object" - }, - "helm-values.cainjector.serviceLabels": { - "default": {}, - "description": "Optional additional labels to add to the CA Injector metrics Service.", - "type": "object" - }, - "helm-values.cainjector.strategy": { - "default": {}, - "description": "Deployment update strategy for the cert-manager cainjector deployment. For more information, see the [Kubernetes documentation](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy).\n\nFor example:\nstrategy:\n type: RollingUpdate\n rollingUpdate:\n maxSurge: 0\n maxUnavailable: 1", - "type": "object" - }, - "helm-values.cainjector.tolerations": { - "default": [], - "description": "A list of Kubernetes Tolerations, if required. For more information, see [Toleration v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#toleration-v1-core).\n\nFor example:\ntolerations:\n- key: foo.bar.com/role\n operator: Equal\n value: master\n effect: NoSchedule", - "items": {}, - "type": "array" - }, - "helm-values.cainjector.topologySpreadConstraints": { - "default": [], - "description": "A list of Kubernetes TopologySpreadConstraints, if required. For more information, see [Topology spread constraint v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#topologyspreadconstraint-v1-core).\n\nFor example:\ntopologySpreadConstraints:\n- maxSkew: 2\n topologyKey: topology.kubernetes.io/zone\n whenUnsatisfiable: ScheduleAnyway\n labelSelector:\n matchLabels:\n app.kubernetes.io/instance: cert-manager\n app.kubernetes.io/component: controller", - "items": {}, - "type": "array" - }, - "helm-values.cainjector.volumeMounts": { - "default": [], - "description": "Additional volume mounts to add to the cert-manager controller container.", - "items": {}, - "type": "array" - }, - "helm-values.cainjector.volumes": { - "default": [], - "description": "Additional volumes to add to the cert-manager controller pod.", - "items": {}, - "type": "array" - }, - "helm-values.clusterResourceNamespace": { - "default": "", - "description": "Override the namespace used to store DNS provider credentials etc. for ClusterIssuer resources. By default, the same namespace as cert-manager is deployed within is used. This namespace will not be automatically created by the Helm chart.", - "type": "string" - }, - "helm-values.config": { - "default": {}, - "description": "This property is used to configure options for the controller pod. This allows setting options that would usually be provided using flags.\n\nIf `apiVersion` and `kind` are unspecified they default to the current latest version (currently `controller.config.cert-manager.io/v1alpha1`). You can pin the version by specifying the `apiVersion` yourself.\n\nFor example:\nconfig:\n apiVersion: controller.config.cert-manager.io/v1alpha1\n kind: ControllerConfiguration\n logging:\n verbosity: 2\n format: text\n leaderElectionConfig:\n namespace: kube-system\n kubernetesAPIQPS: 9000\n kubernetesAPIBurst: 9000\n numberOfConcurrentWorkers: 200\n featureGates:\n AdditionalCertificateOutputFormats: true\n DisallowInsecureCSRUsageDefinition: true\n ExperimentalCertificateSigningRequestControllers: true\n ExperimentalGatewayAPISupport: true\n LiteralCertificateSubject: true\n SecretsFilteredCaching: true\n ServerSideApply: true\n StableCertificateRequestName: true\n UseCertificateRequestBasicConstraints: true\n ValidateCAA: true\n # Configure the metrics server for TLS\n # See https://cert-manager.io/docs/devops-tips/prometheus-metrics/#tls\n metricsTLSConfig:\n dynamic:\n secretNamespace: \"cert-manager\"\n secretName: \"cert-manager-metrics-ca\"\n dnsNames:\n - cert-manager-metrics", - "type": "object" - }, - "helm-values.containerSecurityContext": { - "default": { - "allowPrivilegeEscalation": false, - "capabilities": { - "drop": [ - "ALL" - ] - }, - "readOnlyRootFilesystem": true - }, - "description": "Container Security Context to be set on the controller component container. For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/).", - "type": "object" - }, - "helm-values.crds": { - "additionalProperties": false, - "properties": { - "enabled": { - "$ref": "#/$defs/helm-values.crds.enabled" - }, - "keep": { - "$ref": "#/$defs/helm-values.crds.keep" - } - }, - "type": "object" - }, - "helm-values.crds.enabled": { - "default": false, - "description": "This option decides if the CRDs should be installed as part of the Helm installation.", - "type": "boolean" - }, - "helm-values.crds.keep": { - "default": true, - "description": "This option makes it so that the \"helm.sh/resource-policy\": keep annotation is added to the CRD. This will prevent Helm from uninstalling the CRD when the Helm release is uninstalled. WARNING: when the CRDs are removed, all cert-manager custom resources\n(Certificates, Issuers, ...) will be removed too by the garbage collector.", - "type": "boolean" - }, - "helm-values.creator": { - "default": "helm", - "description": "Field used by our release pipeline to produce the static manifests. The field defaults to \"helm\" but is set to \"static\" when we render the static YAML manifests.", - "type": "string" - }, - "helm-values.deploymentAnnotations": { - "description": "Optional additional annotations to add to the controller Deployment.", - "type": "object" - }, - "helm-values.disableAutoApproval": { - "default": false, - "description": "Option to disable cert-manager's build-in auto-approver. The auto-approver approves all CertificateRequests that reference issuers matching the 'approveSignerNames' option. This 'disableAutoApproval' option is useful when you want to make all approval decisions using a different approver (like approver-policy - https://github.com/cert-manager/approver-policy).", - "type": "boolean" - }, - "helm-values.dns01RecursiveNameservers": { - "default": "", - "description": "A comma-separated string with the host and port of the recursive nameservers cert-manager should query.", - "type": "string" - }, - "helm-values.dns01RecursiveNameserversOnly": { - "default": false, - "description": "Forces cert-manager to use only the recursive nameservers for verification. Enabling this option could cause the DNS01 self check to take longer owing to caching performed by the recursive nameservers.", - "type": "boolean" - }, - "helm-values.enableCertificateOwnerRef": { - "default": false, - "description": "When this flag is enabled, secrets will be automatically removed when the certificate resource is deleted.", - "type": "boolean" - }, - "helm-values.enableServiceLinks": { - "default": false, - "description": "enableServiceLinks indicates whether information about services should be injected into the pod's environment variables, matching the syntax of Docker links.", - "type": "boolean" - }, - "helm-values.enabled": { - "default": true, - "description": "Field that can be used as a condition when cert-manager is a dependency. This definition is only here as a placeholder such that it is included in the json schema. See https://helm.sh/docs/chart_best_practices/dependencies/#conditions-and-tags for more info.", - "type": "boolean" - }, - "helm-values.extraArgs": { - "default": [], - "description": "Additional command line flags to pass to cert-manager controller binary. To see all available flags run `docker run quay.io/jetstack/cert-manager-controller: --help`.\n\nUse this flag to enable or disable arbitrary controllers. For example, to disable the CertificateRequests approver.\n\nFor example:\nextraArgs:\n - --controllers=*,-certificaterequests-approver", - "items": {}, - "type": "array" - }, - "helm-values.extraEnv": { - "default": [], - "description": "Additional environment variables to pass to cert-manager controller binary.\nFor example:\nextraEnv:\n- name: SOME_VAR\n value: 'some value'", - "items": {}, - "type": "array" - }, - "helm-values.extraObjects": { - "default": [], - "description": "Create dynamic manifests via values.\n\nFor example:\nextraObjects:\n - |\n apiVersion: v1\n kind: ConfigMap\n metadata:\n name: '{{ template \"cert-manager.fullname\" . }}-extra-configmap'", - "items": {}, - "type": "array" - }, - "helm-values.featureGates": { - "default": "", - "description": "A comma-separated list of feature gates that should be enabled on the controller pod.", - "type": "string" - }, - "helm-values.fullnameOverride": { - "description": "Override the \"cert-manager.fullname\" value. This value is used as part of most of the names of the resources created by this Helm chart.", - "type": "string" - }, - "helm-values.global": { - "description": "Global values shared across all (sub)charts", - "properties": { - "commonLabels": { - "$ref": "#/$defs/helm-values.global.commonLabels" - }, - "imagePullSecrets": { - "$ref": "#/$defs/helm-values.global.imagePullSecrets" - }, - "leaderElection": { - "$ref": "#/$defs/helm-values.global.leaderElection" - }, - "logLevel": { - "$ref": "#/$defs/helm-values.global.logLevel" - }, - "podSecurityPolicy": { - "$ref": "#/$defs/helm-values.global.podSecurityPolicy" - }, - "priorityClassName": { - "$ref": "#/$defs/helm-values.global.priorityClassName" - }, - "rbac": { - "$ref": "#/$defs/helm-values.global.rbac" - }, - "revisionHistoryLimit": { - "$ref": "#/$defs/helm-values.global.revisionHistoryLimit" - } - }, - "type": "object" - }, - "helm-values.global.commonLabels": { - "default": {}, - "description": "Labels to apply to all resources.\nPlease note that this does not add labels to the resources created dynamically by the controllers. For these resources, you have to add the labels in the template in the cert-manager custom resource: For example, podTemplate/ ingressTemplate in ACMEChallengeSolverHTTP01Ingress. For more information, see the [cert-manager documentation](https://cert-manager.io/docs/reference/api-docs/#acme.cert-manager.io/v1.ACMEChallengeSolverHTTP01Ingress).\nFor example, secretTemplate in CertificateSpec\nFor more information, see the [cert-manager documentation](https://cert-manager.io/docs/reference/api-docs/#cert-manager.io/v1.CertificateSpec).", - "type": "object" - }, - "helm-values.global.imagePullSecrets": { - "default": [], - "description": "Reference to one or more secrets to be used when pulling images. For more information, see [Pull an Image from a Private Registry](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/).\n\nFor example:\nimagePullSecrets:\n - name: \"image-pull-secret\"", - "items": {}, - "type": "array" - }, - "helm-values.global.leaderElection": { - "properties": { - "leaseDuration": { - "$ref": "#/$defs/helm-values.global.leaderElection.leaseDuration" - }, - "namespace": { - "$ref": "#/$defs/helm-values.global.leaderElection.namespace" - }, - "renewDeadline": { - "$ref": "#/$defs/helm-values.global.leaderElection.renewDeadline" - }, - "retryPeriod": { - "$ref": "#/$defs/helm-values.global.leaderElection.retryPeriod" - } - }, - "type": "object" - }, - "helm-values.global.leaderElection.leaseDuration": { - "description": "The duration that non-leader candidates will wait after observing a leadership renewal until attempting to acquire leadership of a led but unrenewed leader slot. This is effectively the maximum duration that a leader can be stopped before it is replaced by another candidate.", - "type": "string" - }, - "helm-values.global.leaderElection.namespace": { - "default": "kube-system", - "description": "Override the namespace used for the leader election lease.", - "type": "string" - }, - "helm-values.global.leaderElection.renewDeadline": { - "description": "The interval between attempts by the acting master to renew a leadership slot before it stops leading. This must be less than or equal to the lease duration.", - "type": "string" - }, - "helm-values.global.leaderElection.retryPeriod": { - "description": "The duration the clients should wait between attempting acquisition and renewal of a leadership.", - "type": "string" - }, - "helm-values.global.logLevel": { - "default": 2, - "description": "Set the verbosity of cert-manager. A range of 0 - 6, with 6 being the most verbose.", - "type": "number" - }, - "helm-values.global.podSecurityPolicy": { - "properties": { - "enabled": { - "$ref": "#/$defs/helm-values.global.podSecurityPolicy.enabled" - }, - "useAppArmor": { - "$ref": "#/$defs/helm-values.global.podSecurityPolicy.useAppArmor" - } - }, - "type": "object" - }, - "helm-values.global.podSecurityPolicy.enabled": { - "default": false, - "description": "Create PodSecurityPolicy for cert-manager.\n\nNote that PodSecurityPolicy was deprecated in Kubernetes 1.21 and removed in Kubernetes 1.25.", - "type": "boolean" - }, - "helm-values.global.podSecurityPolicy.useAppArmor": { - "default": true, - "description": "Configure the PodSecurityPolicy to use AppArmor.", - "type": "boolean" - }, - "helm-values.global.priorityClassName": { - "default": "", - "description": "The optional priority class to be used for the cert-manager pods.", - "type": "string" - }, - "helm-values.global.rbac": { - "properties": { - "aggregateClusterRoles": { - "$ref": "#/$defs/helm-values.global.rbac.aggregateClusterRoles" - }, - "create": { - "$ref": "#/$defs/helm-values.global.rbac.create" - } - }, - "type": "object" - }, - "helm-values.global.rbac.aggregateClusterRoles": { - "default": true, - "description": "Aggregate ClusterRoles to Kubernetes default user-facing roles. For more information, see [User-facing roles](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles)", - "type": "boolean" - }, - "helm-values.global.rbac.create": { - "default": true, - "description": "Create required ClusterRoles and ClusterRoleBindings for cert-manager.", - "type": "boolean" - }, - "helm-values.global.revisionHistoryLimit": { - "description": "The number of old ReplicaSets to retain to allow rollback (if not set, the default Kubernetes value is set to 10).", - "type": "number" - }, - "helm-values.hostAliases": { - "default": [], - "description": "Optional hostAliases for cert-manager-controller pods. May be useful when performing ACME DNS-01 self checks.", - "items": {}, - "type": "array" - }, - "helm-values.http_proxy": { - "description": "Configures the HTTP_PROXY environment variable where a HTTP proxy is required.", - "type": "string" - }, - "helm-values.https_proxy": { - "description": "Configures the HTTPS_PROXY environment variable where a HTTP proxy is required.", - "type": "string" - }, - "helm-values.image": { - "additionalProperties": false, - "properties": { - "digest": { - "$ref": "#/$defs/helm-values.image.digest" - }, - "pullPolicy": { - "$ref": "#/$defs/helm-values.image.pullPolicy" - }, - "registry": { - "$ref": "#/$defs/helm-values.image.registry" - }, - "repository": { - "$ref": "#/$defs/helm-values.image.repository" - }, - "tag": { - "$ref": "#/$defs/helm-values.image.tag" - } - }, - "type": "object" - }, - "helm-values.image.digest": { - "description": "Setting a digest will override any tag.", - "type": "string" - }, - "helm-values.image.pullPolicy": { - "default": "IfNotPresent", - "description": "Kubernetes imagePullPolicy on Deployment.", - "type": "string" - }, - "helm-values.image.registry": { - "description": "The container registry to pull the manager image from.", - "type": "string" - }, - "helm-values.image.repository": { - "default": "quay.io/jetstack/cert-manager-controller", - "description": "The container image for the cert-manager controller.", - "type": "string" - }, - "helm-values.image.tag": { - "description": "Override the image tag to deploy by setting this variable. If no value is set, the chart's appVersion is used.", - "type": "string" - }, - "helm-values.ingressShim": { - "additionalProperties": false, - "properties": { - "defaultIssuerGroup": { - "$ref": "#/$defs/helm-values.ingressShim.defaultIssuerGroup" - }, - "defaultIssuerKind": { - "$ref": "#/$defs/helm-values.ingressShim.defaultIssuerKind" - }, - "defaultIssuerName": { - "$ref": "#/$defs/helm-values.ingressShim.defaultIssuerName" - } - }, - "type": "object" - }, - "helm-values.ingressShim.defaultIssuerGroup": { - "description": "Optional default issuer group to use for ingress resources.", - "type": "string" - }, - "helm-values.ingressShim.defaultIssuerKind": { - "description": "Optional default issuer kind to use for ingress resources.", - "type": "string" - }, - "helm-values.ingressShim.defaultIssuerName": { - "description": "Optional default issuer to use for ingress resources.", - "type": "string" - }, - "helm-values.installCRDs": { - "default": false, - "description": "This option is equivalent to setting crds.enabled=true and crds.keep=true. Deprecated: use crds.enabled and crds.keep instead.", - "type": "boolean" - }, - "helm-values.livenessProbe": { - "default": { - "enabled": true, - "failureThreshold": 8, - "initialDelaySeconds": 10, - "periodSeconds": 10, - "successThreshold": 1, - "timeoutSeconds": 15 - }, - "description": "LivenessProbe settings for the controller container of the controller Pod.\n\nThis is enabled by default, in order to enable the clock-skew liveness probe that restarts the controller in case of a skew between the system clock and the monotonic clock. LivenessProbe durations and thresholds are based on those used for the Kubernetes controller-manager. For more information see the following on the\n[Kubernetes GitHub repository](https://github.com/kubernetes/kubernetes/blob/806b30170c61a38fedd54cc9ede4cd6275a1ad3b/cmd/kubeadm/app/util/staticpod/utils.go#L241-L245)", - "type": "object" - }, - "helm-values.maxConcurrentChallenges": { - "default": 60, - "description": "The maximum number of challenges that can be scheduled as 'processing' at once.", - "type": "number" - }, - "helm-values.nameOverride": { - "description": "Override the \"cert-manager.name\" value, which is used to annotate some of the resources that are created by this Chart (using \"app.kubernetes.io/name\"). NOTE: There are some inconsistencies in the Helm chart when it comes to these annotations (some resources use eg. \"cainjector.name\" which resolves to the value \"cainjector\").", - "type": "string" - }, - "helm-values.namespace": { - "default": "", - "description": "This namespace allows you to define where the services are installed into. If not set then they use the namespace of the release. This is helpful when installing cert manager as a chart dependency (sub chart).", - "type": "string" - }, - "helm-values.no_proxy": { - "description": "Configures the NO_PROXY environment variable where a HTTP proxy is required, but certain domains should be excluded.", - "type": "string" - }, - "helm-values.nodeSelector": { - "default": { - "kubernetes.io/os": "linux" - }, - "description": "The nodeSelector on Pods tells Kubernetes to schedule Pods on the nodes with matching labels. For more information, see [Assigning Pods to Nodes](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/).\n\nThis default ensures that Pods are only scheduled to Linux nodes. It prevents Pods being scheduled to Windows nodes in a mixed OS cluster.", - "type": "object" - }, - "helm-values.podAnnotations": { - "description": "Optional additional annotations to add to the controller Pods.", - "type": "object" - }, - "helm-values.podDisruptionBudget": { - "additionalProperties": false, - "properties": { - "enabled": { - "$ref": "#/$defs/helm-values.podDisruptionBudget.enabled" - }, - "maxUnavailable": { - "$ref": "#/$defs/helm-values.podDisruptionBudget.maxUnavailable" - }, - "minAvailable": { - "$ref": "#/$defs/helm-values.podDisruptionBudget.minAvailable" - } - }, - "type": "object" - }, - "helm-values.podDisruptionBudget.enabled": { - "default": false, - "description": "Enable or disable the PodDisruptionBudget resource.\n\nThis prevents downtime during voluntary disruptions such as during a Node upgrade. For example, the PodDisruptionBudget will block `kubectl drain` if it is used on the Node where the only remaining cert-manager\nPod is currently running.", - "type": "boolean" - }, - "helm-values.podDisruptionBudget.maxUnavailable": { - "description": "This configures the maximum unavailable pods for disruptions. It can either be set to an integer (e.g. 1) or a percentage value (e.g. 25%). it cannot be used if `minAvailable` is set." - }, - "helm-values.podDisruptionBudget.minAvailable": { - "description": "This configures the minimum available pods for disruptions. It can either be set to an integer (e.g. 1) or a percentage value (e.g. 25%).\nIt cannot be used if `maxUnavailable` is set." - }, - "helm-values.podDnsConfig": { - "description": "Pod DNS configuration. The podDnsConfig field is optional and can work with any podDnsPolicy settings. However, when a Pod's dnsPolicy is set to \"None\", the dnsConfig field has to be specified. For more information, see [Pod's DNS Config](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-dns-config).", - "type": "object" - }, - "helm-values.podDnsPolicy": { - "description": "Pod DNS policy.\nFor more information, see [Pod's DNS Policy](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy).", - "type": "string" - }, - "helm-values.podLabels": { - "default": {}, - "description": "Optional additional labels to add to the controller Pods.", - "type": "object" - }, - "helm-values.prometheus": { - "additionalProperties": false, - "properties": { - "enabled": { - "$ref": "#/$defs/helm-values.prometheus.enabled" - }, - "podmonitor": { - "$ref": "#/$defs/helm-values.prometheus.podmonitor" - }, - "servicemonitor": { - "$ref": "#/$defs/helm-values.prometheus.servicemonitor" - } - }, - "type": "object" - }, - "helm-values.prometheus.enabled": { - "default": true, - "description": "Enable Prometheus monitoring for the cert-manager controller and webhook. If you use the Prometheus Operator, set prometheus.podmonitor.enabled or prometheus.servicemonitor.enabled, to create a PodMonitor or a\nServiceMonitor resource.\nOtherwise, 'prometheus.io' annotations are added to the cert-manager and cert-manager-webhook Deployments. Note that you can not enable both PodMonitor and ServiceMonitor as they are mutually exclusive. Enabling both will result in an error.", - "type": "boolean" - }, - "helm-values.prometheus.podmonitor": { - "additionalProperties": false, - "properties": { - "annotations": { - "$ref": "#/$defs/helm-values.prometheus.podmonitor.annotations" - }, - "enabled": { - "$ref": "#/$defs/helm-values.prometheus.podmonitor.enabled" - }, - "endpointAdditionalProperties": { - "$ref": "#/$defs/helm-values.prometheus.podmonitor.endpointAdditionalProperties" - }, - "honorLabels": { - "$ref": "#/$defs/helm-values.prometheus.podmonitor.honorLabels" - }, - "interval": { - "$ref": "#/$defs/helm-values.prometheus.podmonitor.interval" - }, - "labels": { - "$ref": "#/$defs/helm-values.prometheus.podmonitor.labels" - }, - "namespace": { - "$ref": "#/$defs/helm-values.prometheus.podmonitor.namespace" - }, - "path": { - "$ref": "#/$defs/helm-values.prometheus.podmonitor.path" - }, - "prometheusInstance": { - "$ref": "#/$defs/helm-values.prometheus.podmonitor.prometheusInstance" - }, - "scrapeTimeout": { - "$ref": "#/$defs/helm-values.prometheus.podmonitor.scrapeTimeout" - } - }, - "type": "object" - }, - "helm-values.prometheus.podmonitor.annotations": { - "default": {}, - "description": "Additional annotations to add to the PodMonitor.", - "type": "object" - }, - "helm-values.prometheus.podmonitor.enabled": { - "default": false, - "description": "Create a PodMonitor to add cert-manager to Prometheus.", - "type": "boolean" - }, - "helm-values.prometheus.podmonitor.endpointAdditionalProperties": { - "default": {}, - "description": "EndpointAdditionalProperties allows setting additional properties on the endpoint such as relabelings, metricRelabelings etc.\n\nFor example:\nendpointAdditionalProperties:\n relabelings:\n - action: replace\n sourceLabels:\n - __meta_kubernetes_pod_node_name\n targetLabel: instance\n # Configure the PodMonitor for TLS connections\n # See https://cert-manager.io/docs/devops-tips/prometheus-metrics/#tls\n scheme: https\n tlsConfig:\n serverName: cert-manager-metrics\n ca:\n secret:\n name: cert-manager-metrics-ca\n key: \"tls.crt\"", - "type": "object" - }, - "helm-values.prometheus.podmonitor.honorLabels": { - "default": false, - "description": "Keep labels from scraped data, overriding server-side labels.", - "type": "boolean" - }, - "helm-values.prometheus.podmonitor.interval": { - "default": "60s", - "description": "The interval to scrape metrics.", - "type": "string" - }, - "helm-values.prometheus.podmonitor.labels": { - "default": {}, - "description": "Additional labels to add to the PodMonitor.", - "type": "object" - }, - "helm-values.prometheus.podmonitor.namespace": { - "description": "The namespace that the pod monitor should live in, defaults to the cert-manager namespace.", - "type": "string" - }, - "helm-values.prometheus.podmonitor.path": { - "default": "/metrics", - "description": "The path to scrape for metrics.", - "type": "string" - }, - "helm-values.prometheus.podmonitor.prometheusInstance": { - "default": "default", - "description": "Specifies the `prometheus` label on the created PodMonitor. This is used when different Prometheus instances have label selectors matching different PodMonitors.", - "type": "string" - }, - "helm-values.prometheus.podmonitor.scrapeTimeout": { - "default": "30s", - "description": "The timeout before a metrics scrape fails.", - "type": "string" - }, - "helm-values.prometheus.servicemonitor": { - "additionalProperties": false, - "properties": { - "annotations": { - "$ref": "#/$defs/helm-values.prometheus.servicemonitor.annotations" - }, - "enabled": { - "$ref": "#/$defs/helm-values.prometheus.servicemonitor.enabled" - }, - "endpointAdditionalProperties": { - "$ref": "#/$defs/helm-values.prometheus.servicemonitor.endpointAdditionalProperties" - }, - "honorLabels": { - "$ref": "#/$defs/helm-values.prometheus.servicemonitor.honorLabels" - }, - "interval": { - "$ref": "#/$defs/helm-values.prometheus.servicemonitor.interval" - }, - "labels": { - "$ref": "#/$defs/helm-values.prometheus.servicemonitor.labels" - }, - "namespace": { - "$ref": "#/$defs/helm-values.prometheus.servicemonitor.namespace" - }, - "path": { - "$ref": "#/$defs/helm-values.prometheus.servicemonitor.path" - }, - "prometheusInstance": { - "$ref": "#/$defs/helm-values.prometheus.servicemonitor.prometheusInstance" - }, - "scrapeTimeout": { - "$ref": "#/$defs/helm-values.prometheus.servicemonitor.scrapeTimeout" - }, - "targetPort": { - "$ref": "#/$defs/helm-values.prometheus.servicemonitor.targetPort" - } - }, - "type": "object" - }, - "helm-values.prometheus.servicemonitor.annotations": { - "default": {}, - "description": "Additional annotations to add to the ServiceMonitor.", - "type": "object" - }, - "helm-values.prometheus.servicemonitor.enabled": { - "default": false, - "description": "Create a ServiceMonitor to add cert-manager to Prometheus.", - "type": "boolean" - }, - "helm-values.prometheus.servicemonitor.endpointAdditionalProperties": { - "default": {}, - "description": "EndpointAdditionalProperties allows setting additional properties on the endpoint such as relabelings, metricRelabelings etc.\n\nFor example:\nendpointAdditionalProperties:\n relabelings:\n - action: replace\n sourceLabels:\n - __meta_kubernetes_pod_node_name\n targetLabel: instance", - "type": "object" - }, - "helm-values.prometheus.servicemonitor.honorLabels": { - "default": false, - "description": "Keep labels from scraped data, overriding server-side labels.", - "type": "boolean" - }, - "helm-values.prometheus.servicemonitor.interval": { - "default": "60s", - "description": "The interval to scrape metrics.", - "type": "string" - }, - "helm-values.prometheus.servicemonitor.labels": { - "default": {}, - "description": "Additional labels to add to the ServiceMonitor.", - "type": "object" - }, - "helm-values.prometheus.servicemonitor.namespace": { - "description": "The namespace that the service monitor should live in, defaults to the cert-manager namespace.", - "type": "string" - }, - "helm-values.prometheus.servicemonitor.path": { - "default": "/metrics", - "description": "The path to scrape for metrics.", - "type": "string" - }, - "helm-values.prometheus.servicemonitor.prometheusInstance": { - "default": "default", - "description": "Specifies the `prometheus` label on the created ServiceMonitor. This is used when different Prometheus instances have label selectors matching different ServiceMonitors.", - "type": "string" - }, - "helm-values.prometheus.servicemonitor.scrapeTimeout": { - "default": "30s", - "description": "The timeout before a metrics scrape fails.", - "type": "string" - }, - "helm-values.prometheus.servicemonitor.targetPort": { - "default": 9402, - "description": "The target port to set on the ServiceMonitor. This must match the port that the cert-manager controller is listening on for metrics.", - "type": "number" - }, - "helm-values.replicaCount": { - "default": 1, - "description": "The number of replicas of the cert-manager controller to run.\n\nThe default is 1, but in production set this to 2 or 3 to provide high availability.\n\nIf `replicas > 1`, consider setting `podDisruptionBudget.enabled=true`.\n\nNote that cert-manager uses leader election to ensure that there can only be a single instance active at a time.", - "type": "number" - }, - "helm-values.resources": { - "default": {}, - "description": "Resources to provide to the cert-manager controller pod.\n\nFor example:\nrequests:\n cpu: 10m\n memory: 32Mi\nFor more information, see [Resource Management for Pods and Containers](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/).", - "type": "object" - }, - "helm-values.securityContext": { - "default": { - "runAsNonRoot": true, - "seccompProfile": { - "type": "RuntimeDefault" - } - }, - "description": "Pod Security Context.\nFor more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/).", - "type": "object" - }, - "helm-values.serviceAccount": { - "additionalProperties": false, - "properties": { - "annotations": { - "$ref": "#/$defs/helm-values.serviceAccount.annotations" - }, - "automountServiceAccountToken": { - "$ref": "#/$defs/helm-values.serviceAccount.automountServiceAccountToken" - }, - "create": { - "$ref": "#/$defs/helm-values.serviceAccount.create" - }, - "labels": { - "$ref": "#/$defs/helm-values.serviceAccount.labels" - }, - "name": { - "$ref": "#/$defs/helm-values.serviceAccount.name" - } - }, - "type": "object" - }, - "helm-values.serviceAccount.annotations": { - "description": "Optional additional annotations to add to the controller's Service Account.", - "type": "object" - }, - "helm-values.serviceAccount.automountServiceAccountToken": { - "default": true, - "description": "Automount API credentials for a Service Account.", - "type": "boolean" - }, - "helm-values.serviceAccount.create": { - "default": true, - "description": "Specifies whether a service account should be created.", - "type": "boolean" - }, - "helm-values.serviceAccount.labels": { - "description": "Optional additional labels to add to the controller's Service Account.", - "type": "object" - }, - "helm-values.serviceAccount.name": { - "description": "The name of the service account to use.\nIf not set and create is true, a name is generated using the fullname template.", - "type": "string" - }, - "helm-values.serviceAnnotations": { - "description": "Optional annotations to add to the controller Service.", - "type": "object" - }, - "helm-values.serviceIPFamilies": { - "description": "Optionally set the IP families for the controller Service that should be supported, in the order in which they should be applied to ClusterIP. Can be IPv4 and/or IPv6.", - "items": {}, - "type": "array" - }, - "helm-values.serviceIPFamilyPolicy": { - "description": "Optionally set the IP family policy for the controller Service to configure dual-stack; see [Configure dual-stack](https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services).", - "type": "string" - }, - "helm-values.serviceLabels": { - "description": "Optional additional labels to add to the controller Service.", - "type": "object" - }, - "helm-values.startupapicheck": { - "additionalProperties": false, - "properties": { - "affinity": { - "$ref": "#/$defs/helm-values.startupapicheck.affinity" - }, - "automountServiceAccountToken": { - "$ref": "#/$defs/helm-values.startupapicheck.automountServiceAccountToken" - }, - "backoffLimit": { - "$ref": "#/$defs/helm-values.startupapicheck.backoffLimit" - }, - "containerSecurityContext": { - "$ref": "#/$defs/helm-values.startupapicheck.containerSecurityContext" - }, - "enableServiceLinks": { - "$ref": "#/$defs/helm-values.startupapicheck.enableServiceLinks" - }, - "enabled": { - "$ref": "#/$defs/helm-values.startupapicheck.enabled" - }, - "extraArgs": { - "$ref": "#/$defs/helm-values.startupapicheck.extraArgs" - }, - "extraEnv": { - "$ref": "#/$defs/helm-values.startupapicheck.extraEnv" - }, - "image": { - "$ref": "#/$defs/helm-values.startupapicheck.image" - }, - "jobAnnotations": { - "$ref": "#/$defs/helm-values.startupapicheck.jobAnnotations" - }, - "nodeSelector": { - "$ref": "#/$defs/helm-values.startupapicheck.nodeSelector" - }, - "podAnnotations": { - "$ref": "#/$defs/helm-values.startupapicheck.podAnnotations" - }, - "podLabels": { - "$ref": "#/$defs/helm-values.startupapicheck.podLabels" - }, - "rbac": { - "$ref": "#/$defs/helm-values.startupapicheck.rbac" - }, - "resources": { - "$ref": "#/$defs/helm-values.startupapicheck.resources" - }, - "securityContext": { - "$ref": "#/$defs/helm-values.startupapicheck.securityContext" - }, - "serviceAccount": { - "$ref": "#/$defs/helm-values.startupapicheck.serviceAccount" - }, - "timeout": { - "$ref": "#/$defs/helm-values.startupapicheck.timeout" - }, - "tolerations": { - "$ref": "#/$defs/helm-values.startupapicheck.tolerations" - }, - "volumeMounts": { - "$ref": "#/$defs/helm-values.startupapicheck.volumeMounts" - }, - "volumes": { - "$ref": "#/$defs/helm-values.startupapicheck.volumes" - } - }, - "type": "object" - }, - "helm-values.startupapicheck.affinity": { - "default": {}, - "description": "A Kubernetes Affinity, if required. For more information, see [Affinity v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#affinity-v1-core).\nFor example:\naffinity:\n nodeAffinity:\n requiredDuringSchedulingIgnoredDuringExecution:\n nodeSelectorTerms:\n - matchExpressions:\n - key: foo.bar.com/role\n operator: In\n values:\n - master", - "type": "object" - }, - "helm-values.startupapicheck.automountServiceAccountToken": { - "description": "Automounting API credentials for a particular pod.", - "type": "boolean" - }, - "helm-values.startupapicheck.backoffLimit": { - "default": 4, - "description": "Job backoffLimit", - "type": "number" - }, - "helm-values.startupapicheck.containerSecurityContext": { - "default": { - "allowPrivilegeEscalation": false, - "capabilities": { - "drop": [ - "ALL" - ] - }, - "readOnlyRootFilesystem": true - }, - "description": "Container Security Context to be set on the controller component container. For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/).", - "type": "object" - }, - "helm-values.startupapicheck.enableServiceLinks": { - "default": false, - "description": "enableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links.", - "type": "boolean" - }, - "helm-values.startupapicheck.enabled": { - "default": true, - "description": "Enables the startup api check.", - "type": "boolean" - }, - "helm-values.startupapicheck.extraArgs": { - "default": [ - "-v" - ], - "description": "Additional command line flags to pass to startupapicheck binary. To see all available flags run `docker run quay.io/jetstack/cert-manager-startupapicheck: --help`.\n\nVerbose logging is enabled by default so that if startupapicheck fails, you can know what exactly caused the failure. Verbose logs include details of the webhook URL, IP address and TCP connect errors for example.", - "items": {}, - "type": "array" - }, - "helm-values.startupapicheck.extraEnv": { - "default": [], - "description": "Additional environment variables to pass to cert-manager startupapicheck binary.\nFor example:\nextraEnv:\n- name: SOME_VAR\n value: 'some value'", - "items": {}, - "type": "array" - }, - "helm-values.startupapicheck.image": { - "additionalProperties": false, - "properties": { - "digest": { - "$ref": "#/$defs/helm-values.startupapicheck.image.digest" - }, - "pullPolicy": { - "$ref": "#/$defs/helm-values.startupapicheck.image.pullPolicy" - }, - "registry": { - "$ref": "#/$defs/helm-values.startupapicheck.image.registry" - }, - "repository": { - "$ref": "#/$defs/helm-values.startupapicheck.image.repository" - }, - "tag": { - "$ref": "#/$defs/helm-values.startupapicheck.image.tag" - } - }, - "type": "object" - }, - "helm-values.startupapicheck.image.digest": { - "description": "Setting a digest will override any tag.", - "type": "string" - }, - "helm-values.startupapicheck.image.pullPolicy": { - "default": "IfNotPresent", - "description": "Kubernetes imagePullPolicy on Deployment.", - "type": "string" - }, - "helm-values.startupapicheck.image.registry": { - "description": "The container registry to pull the startupapicheck image from.", - "type": "string" - }, - "helm-values.startupapicheck.image.repository": { - "default": "quay.io/jetstack/cert-manager-startupapicheck", - "description": "The container image for the cert-manager startupapicheck.", - "type": "string" - }, - "helm-values.startupapicheck.image.tag": { - "description": "Override the image tag to deploy by setting this variable. If no value is set, the chart's appVersion is used.", - "type": "string" - }, - "helm-values.startupapicheck.jobAnnotations": { - "default": { - "helm.sh/hook": "post-install", - "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded", - "helm.sh/hook-weight": "1" - }, - "description": "Optional additional annotations to add to the startupapicheck Job.", - "type": "object" - }, - "helm-values.startupapicheck.nodeSelector": { - "default": { - "kubernetes.io/os": "linux" - }, - "description": "The nodeSelector on Pods tells Kubernetes to schedule Pods on the nodes with matching labels. For more information, see [Assigning Pods to Nodes](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/).\n\nThis default ensures that Pods are only scheduled to Linux nodes. It prevents Pods being scheduled to Windows nodes in a mixed OS cluster.", - "type": "object" - }, - "helm-values.startupapicheck.podAnnotations": { - "description": "Optional additional annotations to add to the startupapicheck Pods.", - "type": "object" - }, - "helm-values.startupapicheck.podLabels": { - "default": {}, - "description": "Optional additional labels to add to the startupapicheck Pods.", - "type": "object" - }, - "helm-values.startupapicheck.rbac": { - "additionalProperties": false, - "properties": { - "annotations": { - "$ref": "#/$defs/helm-values.startupapicheck.rbac.annotations" - } - }, - "type": "object" - }, - "helm-values.startupapicheck.rbac.annotations": { - "default": { - "helm.sh/hook": "post-install", - "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded", - "helm.sh/hook-weight": "-5" - }, - "description": "annotations for the startup API Check job RBAC and PSP resources.", - "type": "object" - }, - "helm-values.startupapicheck.resources": { - "default": {}, - "description": "Resources to provide to the cert-manager controller pod.\n\nFor example:\nrequests:\n cpu: 10m\n memory: 32Mi\nFor more information, see [Resource Management for Pods and Containers](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/).", - "type": "object" - }, - "helm-values.startupapicheck.securityContext": { - "default": { - "runAsNonRoot": true, - "seccompProfile": { - "type": "RuntimeDefault" - } - }, - "description": "Pod Security Context to be set on the startupapicheck component Pod. For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/).", - "type": "object" - }, - "helm-values.startupapicheck.serviceAccount": { - "additionalProperties": false, - "properties": { - "annotations": { - "$ref": "#/$defs/helm-values.startupapicheck.serviceAccount.annotations" - }, - "automountServiceAccountToken": { - "$ref": "#/$defs/helm-values.startupapicheck.serviceAccount.automountServiceAccountToken" - }, - "create": { - "$ref": "#/$defs/helm-values.startupapicheck.serviceAccount.create" - }, - "labels": { - "$ref": "#/$defs/helm-values.startupapicheck.serviceAccount.labels" - }, - "name": { - "$ref": "#/$defs/helm-values.startupapicheck.serviceAccount.name" - } - }, - "type": "object" - }, - "helm-values.startupapicheck.serviceAccount.annotations": { - "default": { - "helm.sh/hook": "post-install", - "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded", - "helm.sh/hook-weight": "-5" - }, - "description": "Optional additional annotations to add to the Job's Service Account.", - "type": "object" - }, - "helm-values.startupapicheck.serviceAccount.automountServiceAccountToken": { - "default": true, - "description": "Automount API credentials for a Service Account.", - "type": "boolean" - }, - "helm-values.startupapicheck.serviceAccount.create": { - "default": true, - "description": "Specifies whether a service account should be created.", - "type": "boolean" - }, - "helm-values.startupapicheck.serviceAccount.labels": { - "description": "Optional additional labels to add to the startupapicheck's Service Account.", - "type": "object" - }, - "helm-values.startupapicheck.serviceAccount.name": { - "description": "The name of the service account to use.\nIf not set and create is true, a name is generated using the fullname template.", - "type": "string" - }, - "helm-values.startupapicheck.timeout": { - "default": "1m", - "description": "Timeout for 'kubectl check api' command.", - "type": "string" - }, - "helm-values.startupapicheck.tolerations": { - "default": [], - "description": "A list of Kubernetes Tolerations, if required. For more information, see [Toleration v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#toleration-v1-core).\n\nFor example:\ntolerations:\n- key: foo.bar.com/role\n operator: Equal\n value: master\n effect: NoSchedule", - "items": {}, - "type": "array" - }, - "helm-values.startupapicheck.volumeMounts": { - "default": [], - "description": "Additional volume mounts to add to the cert-manager controller container.", - "items": {}, - "type": "array" - }, - "helm-values.startupapicheck.volumes": { - "default": [], - "description": "Additional volumes to add to the cert-manager controller pod.", - "items": {}, - "type": "array" - }, - "helm-values.strategy": { - "default": {}, - "description": "Deployment update strategy for the cert-manager controller deployment. For more information, see the [Kubernetes documentation](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy).\n\nFor example:\nstrategy:\n type: RollingUpdate\n rollingUpdate:\n maxSurge: 0\n maxUnavailable: 1", - "type": "object" - }, - "helm-values.tolerations": { - "default": [], - "description": "A list of Kubernetes Tolerations, if required. For more information, see [Toleration v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#toleration-v1-core).\n\nFor example:\ntolerations:\n- key: foo.bar.com/role\n operator: Equal\n value: master\n effect: NoSchedule", - "items": {}, - "type": "array" - }, - "helm-values.topologySpreadConstraints": { - "default": [], - "description": "A list of Kubernetes TopologySpreadConstraints, if required. For more information, see [Topology spread constraint v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#topologyspreadconstraint-v1-core\n\nFor example:\ntopologySpreadConstraints:\n- maxSkew: 2\n topologyKey: topology.kubernetes.io/zone\n whenUnsatisfiable: ScheduleAnyway\n labelSelector:\n matchLabels:\n app.kubernetes.io/instance: cert-manager\n app.kubernetes.io/component: controller", - "items": {}, - "type": "array" - }, - "helm-values.volumeMounts": { - "default": [], - "description": "Additional volume mounts to add to the cert-manager controller container.", - "items": {}, - "type": "array" - }, - "helm-values.volumes": { - "default": [], - "description": "Additional volumes to add to the cert-manager controller pod.", - "items": {}, - "type": "array" - }, - "helm-values.webhook": { - "additionalProperties": false, - "properties": { - "affinity": { - "$ref": "#/$defs/helm-values.webhook.affinity" - }, - "automountServiceAccountToken": { - "$ref": "#/$defs/helm-values.webhook.automountServiceAccountToken" - }, - "config": { - "$ref": "#/$defs/helm-values.webhook.config" - }, - "containerSecurityContext": { - "$ref": "#/$defs/helm-values.webhook.containerSecurityContext" - }, - "deploymentAnnotations": { - "$ref": "#/$defs/helm-values.webhook.deploymentAnnotations" - }, - "enableServiceLinks": { - "$ref": "#/$defs/helm-values.webhook.enableServiceLinks" - }, - "extraArgs": { - "$ref": "#/$defs/helm-values.webhook.extraArgs" - }, - "extraEnv": { - "$ref": "#/$defs/helm-values.webhook.extraEnv" - }, - "featureGates": { - "$ref": "#/$defs/helm-values.webhook.featureGates" - }, - "hostNetwork": { - "$ref": "#/$defs/helm-values.webhook.hostNetwork" - }, - "image": { - "$ref": "#/$defs/helm-values.webhook.image" - }, - "livenessProbe": { - "$ref": "#/$defs/helm-values.webhook.livenessProbe" - }, - "loadBalancerIP": { - "$ref": "#/$defs/helm-values.webhook.loadBalancerIP" - }, - "mutatingWebhookConfiguration": { - "$ref": "#/$defs/helm-values.webhook.mutatingWebhookConfiguration" - }, - "mutatingWebhookConfigurationAnnotations": { - "$ref": "#/$defs/helm-values.webhook.mutatingWebhookConfigurationAnnotations" - }, - "networkPolicy": { - "$ref": "#/$defs/helm-values.webhook.networkPolicy" - }, - "nodeSelector": { - "$ref": "#/$defs/helm-values.webhook.nodeSelector" - }, - "podAnnotations": { - "$ref": "#/$defs/helm-values.webhook.podAnnotations" - }, - "podDisruptionBudget": { - "$ref": "#/$defs/helm-values.webhook.podDisruptionBudget" - }, - "podLabels": { - "$ref": "#/$defs/helm-values.webhook.podLabels" - }, - "readinessProbe": { - "$ref": "#/$defs/helm-values.webhook.readinessProbe" - }, - "replicaCount": { - "$ref": "#/$defs/helm-values.webhook.replicaCount" - }, - "resources": { - "$ref": "#/$defs/helm-values.webhook.resources" - }, - "securePort": { - "$ref": "#/$defs/helm-values.webhook.securePort" - }, - "securityContext": { - "$ref": "#/$defs/helm-values.webhook.securityContext" - }, - "serviceAccount": { - "$ref": "#/$defs/helm-values.webhook.serviceAccount" - }, - "serviceAnnotations": { - "$ref": "#/$defs/helm-values.webhook.serviceAnnotations" - }, - "serviceIPFamilies": { - "$ref": "#/$defs/helm-values.webhook.serviceIPFamilies" - }, - "serviceIPFamilyPolicy": { - "$ref": "#/$defs/helm-values.webhook.serviceIPFamilyPolicy" - }, - "serviceLabels": { - "$ref": "#/$defs/helm-values.webhook.serviceLabels" - }, - "serviceType": { - "$ref": "#/$defs/helm-values.webhook.serviceType" - }, - "strategy": { - "$ref": "#/$defs/helm-values.webhook.strategy" - }, - "timeoutSeconds": { - "$ref": "#/$defs/helm-values.webhook.timeoutSeconds" - }, - "tolerations": { - "$ref": "#/$defs/helm-values.webhook.tolerations" - }, - "topologySpreadConstraints": { - "$ref": "#/$defs/helm-values.webhook.topologySpreadConstraints" - }, - "url": { - "$ref": "#/$defs/helm-values.webhook.url" - }, - "validatingWebhookConfiguration": { - "$ref": "#/$defs/helm-values.webhook.validatingWebhookConfiguration" - }, - "validatingWebhookConfigurationAnnotations": { - "$ref": "#/$defs/helm-values.webhook.validatingWebhookConfigurationAnnotations" - }, - "volumeMounts": { - "$ref": "#/$defs/helm-values.webhook.volumeMounts" - }, - "volumes": { - "$ref": "#/$defs/helm-values.webhook.volumes" - } - }, - "type": "object" - }, - "helm-values.webhook.affinity": { - "default": {}, - "description": "A Kubernetes Affinity, if required. For more information, see [Affinity v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#affinity-v1-core).\n\nFor example:\naffinity:\n nodeAffinity:\n requiredDuringSchedulingIgnoredDuringExecution:\n nodeSelectorTerms:\n - matchExpressions:\n - key: foo.bar.com/role\n operator: In\n values:\n - master", - "type": "object" - }, - "helm-values.webhook.automountServiceAccountToken": { - "description": "Automounting API credentials for a particular pod.", - "type": "boolean" - }, - "helm-values.webhook.config": { - "default": {}, - "description": "This is used to configure options for the webhook pod. This allows setting options that would usually be provided using flags.\n\nIf `apiVersion` and `kind` are unspecified they default to the current latest version (currently `webhook.config.cert-manager.io/v1alpha1`). You can pin the version by specifying the `apiVersion` yourself.\n\nFor example:\napiVersion: webhook.config.cert-manager.io/v1alpha1\nkind: WebhookConfiguration\n# The port that the webhook listens on for requests.\n# In GKE private clusters, by default Kubernetes apiservers are allowed to\n# talk to the cluster nodes only on 443 and 10250. Configuring\n# securePort: 10250 therefore will work out-of-the-box without needing to add firewall\n# rules or requiring NET_BIND_SERVICE capabilities to bind port numbers < 1000.\n# This should be uncommented and set as a default by the chart once\n# the apiVersion of WebhookConfiguration graduates beyond v1alpha1.\nsecurePort: 10250\n# Configure the metrics server for TLS\n# See https://cert-manager.io/docs/devops-tips/prometheus-metrics/#tls\nmetricsTLSConfig:\n dynamic:\n secretNamespace: \"cert-manager\"\n secretName: \"cert-manager-metrics-ca\"\n dnsNames:\n - cert-manager-metrics", - "type": "object" - }, - "helm-values.webhook.containerSecurityContext": { - "default": { - "allowPrivilegeEscalation": false, - "capabilities": { - "drop": [ - "ALL" - ] - }, - "readOnlyRootFilesystem": true - }, - "description": "Container Security Context to be set on the webhook component container. For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/).", - "type": "object" - }, - "helm-values.webhook.deploymentAnnotations": { - "description": "Optional additional annotations to add to the webhook Deployment.", - "type": "object" - }, - "helm-values.webhook.enableServiceLinks": { - "default": false, - "description": "enableServiceLinks indicates whether information about services should be injected into the pod's environment variables, matching the syntax of Docker links.", - "type": "boolean" - }, - "helm-values.webhook.extraArgs": { - "default": [], - "description": "Additional command line flags to pass to cert-manager webhook binary. To see all available flags run `docker run quay.io/jetstack/cert-manager-webhook: --help`.", - "items": {}, - "type": "array" - }, - "helm-values.webhook.extraEnv": { - "default": [], - "description": "Additional environment variables to pass to cert-manager webhook binary.\nFor example:\nextraEnv:\n- name: SOME_VAR\n value: 'some value'", - "items": {}, - "type": "array" - }, - "helm-values.webhook.featureGates": { - "default": "", - "description": "Comma separated list of feature gates that should be enabled on the webhook pod.", - "type": "string" - }, - "helm-values.webhook.hostNetwork": { - "default": false, - "description": "Specifies if the webhook should be started in hostNetwork mode.\n\nRequired for use in some managed kubernetes clusters (such as AWS EKS) with custom. CNI (such as calico), because control-plane managed by AWS cannot communicate with pods' IP CIDR and admission webhooks are not working\n\nSince the default port for the webhook conflicts with kubelet on the host network, `webhook.securePort` should be changed to an available port if running in hostNetwork mode.", - "type": "boolean" - }, - "helm-values.webhook.image": { - "additionalProperties": false, - "properties": { - "digest": { - "$ref": "#/$defs/helm-values.webhook.image.digest" - }, - "pullPolicy": { - "$ref": "#/$defs/helm-values.webhook.image.pullPolicy" - }, - "registry": { - "$ref": "#/$defs/helm-values.webhook.image.registry" - }, - "repository": { - "$ref": "#/$defs/helm-values.webhook.image.repository" - }, - "tag": { - "$ref": "#/$defs/helm-values.webhook.image.tag" - } - }, - "type": "object" - }, - "helm-values.webhook.image.digest": { - "description": "Setting a digest will override any tag", - "type": "string" - }, - "helm-values.webhook.image.pullPolicy": { - "default": "IfNotPresent", - "description": "Kubernetes imagePullPolicy on Deployment.", - "type": "string" - }, - "helm-values.webhook.image.registry": { - "description": "The container registry to pull the webhook image from.", - "type": "string" - }, - "helm-values.webhook.image.repository": { - "default": "quay.io/jetstack/cert-manager-webhook", - "description": "The container image for the cert-manager webhook", - "type": "string" - }, - "helm-values.webhook.image.tag": { - "description": "Override the image tag to deploy by setting this variable. If no value is set, the chart's appVersion will be used.", - "type": "string" - }, - "helm-values.webhook.livenessProbe": { - "default": { - "failureThreshold": 3, - "initialDelaySeconds": 60, - "periodSeconds": 10, - "successThreshold": 1, - "timeoutSeconds": 1 - }, - "description": "Liveness probe values.\nFor more information, see [Container probes](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes).", - "type": "object" - }, - "helm-values.webhook.loadBalancerIP": { - "description": "Specify the load balancer IP for the created service.", - "type": "string" - }, - "helm-values.webhook.mutatingWebhookConfiguration": { - "additionalProperties": false, - "properties": { - "namespaceSelector": { - "$ref": "#/$defs/helm-values.webhook.mutatingWebhookConfiguration.namespaceSelector" - } - }, - "type": "object" - }, - "helm-values.webhook.mutatingWebhookConfiguration.namespaceSelector": { - "default": {}, - "description": "Configure spec.namespaceSelector for mutating webhooks.", - "type": "object" - }, - "helm-values.webhook.mutatingWebhookConfigurationAnnotations": { - "description": "Optional additional annotations to add to the webhook MutatingWebhookConfiguration.", - "type": "object" - }, - "helm-values.webhook.networkPolicy": { - "additionalProperties": false, - "properties": { - "egress": { - "$ref": "#/$defs/helm-values.webhook.networkPolicy.egress" - }, - "enabled": { - "$ref": "#/$defs/helm-values.webhook.networkPolicy.enabled" - }, - "ingress": { - "$ref": "#/$defs/helm-values.webhook.networkPolicy.ingress" - } - }, - "type": "object" - }, - "helm-values.webhook.networkPolicy.egress": { - "default": [ - { - "ports": [ - { - "port": 80, - "protocol": "TCP" - }, - { - "port": 443, - "protocol": "TCP" - }, - { - "port": 53, - "protocol": "TCP" - }, - { - "port": 53, - "protocol": "UDP" - }, - { - "port": 6443, - "protocol": "TCP" - } - ], - "to": [ - { - "ipBlock": { - "cidr": "0.0.0.0/0" - } - } - ] - } - ], - "description": "Egress rule for the webhook network policy. By default, it allows all outbound traffic to ports 80 and 443, as well as DNS ports.", - "items": {}, - "type": "array" - }, - "helm-values.webhook.networkPolicy.enabled": { - "default": false, - "description": "Create network policies for the webhooks.", - "type": "boolean" - }, - "helm-values.webhook.networkPolicy.ingress": { - "default": [ - { - "from": [ - { - "ipBlock": { - "cidr": "0.0.0.0/0" - } - } - ] - } - ], - "description": "Ingress rule for the webhook network policy. By default, it allows all inbound traffic.", - "items": {}, - "type": "array" - }, - "helm-values.webhook.nodeSelector": { - "default": { - "kubernetes.io/os": "linux" - }, - "description": "The nodeSelector on Pods tells Kubernetes to schedule Pods on the nodes with matching labels. For more information, see [Assigning Pods to Nodes](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/).\n\nThis default ensures that Pods are only scheduled to Linux nodes. It prevents Pods being scheduled to Windows nodes in a mixed OS cluster.", - "type": "object" - }, - "helm-values.webhook.podAnnotations": { - "description": "Optional additional annotations to add to the webhook Pods.", - "type": "object" - }, - "helm-values.webhook.podDisruptionBudget": { - "additionalProperties": false, - "properties": { - "enabled": { - "$ref": "#/$defs/helm-values.webhook.podDisruptionBudget.enabled" - }, - "maxUnavailable": { - "$ref": "#/$defs/helm-values.webhook.podDisruptionBudget.maxUnavailable" - }, - "minAvailable": { - "$ref": "#/$defs/helm-values.webhook.podDisruptionBudget.minAvailable" - } - }, - "type": "object" - }, - "helm-values.webhook.podDisruptionBudget.enabled": { - "default": false, - "description": "Enable or disable the PodDisruptionBudget resource.\n\nThis prevents downtime during voluntary disruptions such as during a Node upgrade. For example, the PodDisruptionBudget will block `kubectl drain` if it is used on the Node where the only remaining cert-manager\nPod is currently running.", - "type": "boolean" - }, - "helm-values.webhook.podDisruptionBudget.maxUnavailable": { - "description": "This property configures the maximum unavailable pods for disruptions. Can either be set to an integer (e.g. 1) or a percentage value (e.g. 25%).\nIt cannot be used if `minAvailable` is set." - }, - "helm-values.webhook.podDisruptionBudget.minAvailable": { - "description": "This property configures the minimum available pods for disruptions. Can either be set to an integer (e.g. 1) or a percentage value (e.g. 25%).\nIt cannot be used if `maxUnavailable` is set." - }, - "helm-values.webhook.podLabels": { - "default": {}, - "description": "Optional additional labels to add to the Webhook Pods.", - "type": "object" - }, - "helm-values.webhook.readinessProbe": { - "default": { - "failureThreshold": 3, - "initialDelaySeconds": 5, - "periodSeconds": 5, - "successThreshold": 1, - "timeoutSeconds": 1 - }, - "description": "Readiness probe values.\nFor more information, see [Container probes](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes).", - "type": "object" - }, - "helm-values.webhook.replicaCount": { - "default": 1, - "description": "Number of replicas of the cert-manager webhook to run.\n\nThe default is 1, but in production set this to 2 or 3 to provide high availability.\n\nIf `replicas > 1`, consider setting `webhook.podDisruptionBudget.enabled=true`.", - "type": "number" - }, - "helm-values.webhook.resources": { - "default": {}, - "description": "Resources to provide to the cert-manager webhook pod.\n\nFor example:\nrequests:\n cpu: 10m\n memory: 32Mi\nFor more information, see [Resource Management for Pods and Containers](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/).", - "type": "object" - }, - "helm-values.webhook.securePort": { - "default": 10250, - "description": "The port that the webhook listens on for requests. In GKE private clusters, by default Kubernetes apiservers are allowed to talk to the cluster nodes only on 443 and 10250. Configuring securePort: 10250, therefore will work out-of-the-box without needing to add firewall rules or requiring NET_BIND_SERVICE capabilities to bind port numbers <1000.", - "type": "number" - }, - "helm-values.webhook.securityContext": { - "default": { - "runAsNonRoot": true, - "seccompProfile": { - "type": "RuntimeDefault" - } - }, - "description": "Pod Security Context to be set on the webhook component Pod. For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/).", - "type": "object" - }, - "helm-values.webhook.serviceAccount": { - "additionalProperties": false, - "properties": { - "annotations": { - "$ref": "#/$defs/helm-values.webhook.serviceAccount.annotations" - }, - "automountServiceAccountToken": { - "$ref": "#/$defs/helm-values.webhook.serviceAccount.automountServiceAccountToken" - }, - "create": { - "$ref": "#/$defs/helm-values.webhook.serviceAccount.create" - }, - "labels": { - "$ref": "#/$defs/helm-values.webhook.serviceAccount.labels" - }, - "name": { - "$ref": "#/$defs/helm-values.webhook.serviceAccount.name" - } - }, - "type": "object" - }, - "helm-values.webhook.serviceAccount.annotations": { - "description": "Optional additional annotations to add to the webhook's Service Account.", - "type": "object" - }, - "helm-values.webhook.serviceAccount.automountServiceAccountToken": { - "default": true, - "description": "Automount API credentials for a Service Account.", - "type": "boolean" - }, - "helm-values.webhook.serviceAccount.create": { - "default": true, - "description": "Specifies whether a service account should be created.", - "type": "boolean" - }, - "helm-values.webhook.serviceAccount.labels": { - "description": "Optional additional labels to add to the webhook's Service Account.", - "type": "object" - }, - "helm-values.webhook.serviceAccount.name": { - "description": "The name of the service account to use.\nIf not set and create is true, a name is generated using the fullname template.", - "type": "string" - }, - "helm-values.webhook.serviceAnnotations": { - "description": "Optional additional annotations to add to the webhook Service.", - "type": "object" - }, - "helm-values.webhook.serviceIPFamilies": { - "default": [], - "description": "Optionally set the IP families for the controller Service that should be supported, in the order in which they should be applied to ClusterIP. Can be IPv4 and/or IPv6.", - "items": {}, - "type": "array" - }, - "helm-values.webhook.serviceIPFamilyPolicy": { - "default": "", - "description": "Optionally set the IP family policy for the controller Service to configure dual-stack; see [Configure dual-stack](https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services).", - "type": "string" - }, - "helm-values.webhook.serviceLabels": { - "default": {}, - "description": "Optional additional labels to add to the Webhook Service.", - "type": "object" - }, - "helm-values.webhook.serviceType": { - "default": "ClusterIP", - "description": "Specifies how the service should be handled. Useful if you want to expose the webhook outside of the cluster. In some cases, the control plane cannot reach internal services.", - "type": "string" - }, - "helm-values.webhook.strategy": { - "default": {}, - "description": "The update strategy for the cert-manager webhook deployment. For more information, see the [Kubernetes documentation](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy)\n\nFor example:\nstrategy:\n type: RollingUpdate\n rollingUpdate:\n maxSurge: 0\n maxUnavailable: 1", - "type": "object" - }, - "helm-values.webhook.timeoutSeconds": { - "default": 30, - "description": "The number of seconds the API server should wait for the webhook to respond before treating the call as a failure. The value must be between 1 and 30 seconds. For more information, see\n[Validating webhook configuration v1](https://kubernetes.io/docs/reference/kubernetes-api/extend-resources/validating-webhook-configuration-v1/).\n\nThe default is set to the maximum value of 30 seconds as users sometimes report that the connection between the K8S API server and the cert-manager webhook server times out. If *this* timeout is reached, the error message will be \"context deadline exceeded\", which doesn't help the user diagnose what phase of the HTTPS connection timed out. For example, it could be during DNS resolution, TCP connection, TLS negotiation, HTTP negotiation, or slow HTTP response from the webhook server. By setting this timeout to its maximum value the underlying timeout error message has more chance of being returned to the end user.", - "type": "number" - }, - "helm-values.webhook.tolerations": { - "default": [], - "description": "A list of Kubernetes Tolerations, if required. For more information, see [Toleration v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#toleration-v1-core).\n\nFor example:\ntolerations:\n- key: foo.bar.com/role\n operator: Equal\n value: master\n effect: NoSchedule", - "items": {}, - "type": "array" - }, - "helm-values.webhook.topologySpreadConstraints": { - "default": [], - "description": "A list of Kubernetes TopologySpreadConstraints, if required. For more information, see [Topology spread constraint v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#topologyspreadconstraint-v1-core).\n\nFor example:\ntopologySpreadConstraints:\n- maxSkew: 2\n topologyKey: topology.kubernetes.io/zone\n whenUnsatisfiable: ScheduleAnyway\n labelSelector:\n matchLabels:\n app.kubernetes.io/instance: cert-manager\n app.kubernetes.io/component: controller", - "items": {}, - "type": "array" - }, - "helm-values.webhook.url": { - "default": {}, - "description": "Overrides the mutating webhook and validating webhook so they reach the webhook service using the `url` field instead of a service.", - "type": "object" - }, - "helm-values.webhook.validatingWebhookConfiguration": { - "additionalProperties": false, - "properties": { - "namespaceSelector": { - "$ref": "#/$defs/helm-values.webhook.validatingWebhookConfiguration.namespaceSelector" - } - }, - "type": "object" - }, - "helm-values.webhook.validatingWebhookConfiguration.namespaceSelector": { - "default": { - "matchExpressions": [ - { - "key": "cert-manager.io/disable-validation", - "operator": "NotIn", - "values": [ - "true" - ] - } - ] - }, - "description": "Configure spec.namespaceSelector for validating webhooks.", - "type": "object" - }, - "helm-values.webhook.validatingWebhookConfigurationAnnotations": { - "description": "Optional additional annotations to add to the webhook ValidatingWebhookConfiguration.", - "type": "object" - }, - "helm-values.webhook.volumeMounts": { - "default": [], - "description": "Additional volume mounts to add to the cert-manager controller container.", - "items": {}, - "type": "array" - }, - "helm-values.webhook.volumes": { - "default": [], - "description": "Additional volumes to add to the cert-manager controller pod.", - "items": {}, - "type": "array" - } - }, - "$ref": "#/$defs/helm-values", - "$schema": "http://json-schema.org/draft-07/schema#" -} diff --git a/packages/system/cert-manager-crds/charts/cert-manager/values.yaml b/packages/system/cert-manager-crds/charts/cert-manager/values.yaml deleted file mode 100644 index 7a1c2953..00000000 --- a/packages/system/cert-manager-crds/charts/cert-manager/values.yaml +++ /dev/null @@ -1,1455 +0,0 @@ -# +docs:section=Global - -# Default values for cert-manager. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. -global: - # Reference to one or more secrets to be used when pulling images. - # For more information, see [Pull an Image from a Private Registry](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/). - # - # For example: - # imagePullSecrets: - # - name: "image-pull-secret" - imagePullSecrets: [] - - # Labels to apply to all resources. - # Please note that this does not add labels to the resources created dynamically by the controllers. - # For these resources, you have to add the labels in the template in the cert-manager custom resource: - # For example, podTemplate/ ingressTemplate in ACMEChallengeSolverHTTP01Ingress - # For more information, see the [cert-manager documentation](https://cert-manager.io/docs/reference/api-docs/#acme.cert-manager.io/v1.ACMEChallengeSolverHTTP01Ingress). - # For example, secretTemplate in CertificateSpec - # For more information, see the [cert-manager documentation](https://cert-manager.io/docs/reference/api-docs/#cert-manager.io/v1.CertificateSpec). - commonLabels: {} - - # The number of old ReplicaSets to retain to allow rollback (if not set, the default Kubernetes value is set to 10). - # +docs:property - # revisionHistoryLimit: 1 - - # The optional priority class to be used for the cert-manager pods. - priorityClassName: "" - - rbac: - # Create required ClusterRoles and ClusterRoleBindings for cert-manager. - create: true - # Aggregate ClusterRoles to Kubernetes default user-facing roles. For more information, see [User-facing roles](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles) - aggregateClusterRoles: true - - podSecurityPolicy: - # Create PodSecurityPolicy for cert-manager. - # - # Note that PodSecurityPolicy was deprecated in Kubernetes 1.21 and removed in Kubernetes 1.25. - enabled: false - # Configure the PodSecurityPolicy to use AppArmor. - useAppArmor: true - - # Set the verbosity of cert-manager. A range of 0 - 6, with 6 being the most verbose. - logLevel: 2 - - leaderElection: - # Override the namespace used for the leader election lease. - namespace: "kube-system" - - # The duration that non-leader candidates will wait after observing a - # leadership renewal until attempting to acquire leadership of a led but - # unrenewed leader slot. This is effectively the maximum duration that a - # leader can be stopped before it is replaced by another candidate. - # +docs:property - # leaseDuration: 60s - - # The interval between attempts by the acting master to renew a leadership - # slot before it stops leading. This must be less than or equal to the - # lease duration. - # +docs:property - # renewDeadline: 40s - - # The duration the clients should wait between attempting acquisition and - # renewal of a leadership. - # +docs:property - # retryPeriod: 15s - -# This option is equivalent to setting crds.enabled=true and crds.keep=true. -# Deprecated: use crds.enabled and crds.keep instead. -installCRDs: false - -crds: - # This option decides if the CRDs should be installed - # as part of the Helm installation. - enabled: false - - # This option makes it so that the "helm.sh/resource-policy": keep - # annotation is added to the CRD. This will prevent Helm from uninstalling - # the CRD when the Helm release is uninstalled. - # WARNING: when the CRDs are removed, all cert-manager custom resources - # (Certificates, Issuers, ...) will be removed too by the garbage collector. - keep: true - -# +docs:section=Controller - -# The number of replicas of the cert-manager controller to run. -# -# The default is 1, but in production set this to 2 or 3 to provide high -# availability. -# -# If `replicas > 1`, consider setting `podDisruptionBudget.enabled=true`. -# -# Note that cert-manager uses leader election to ensure that there can -# only be a single instance active at a time. -replicaCount: 1 - -# Deployment update strategy for the cert-manager controller deployment. -# For more information, see the [Kubernetes documentation](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy). -# -# For example: -# strategy: -# type: RollingUpdate -# rollingUpdate: -# maxSurge: 0 -# maxUnavailable: 1 -strategy: {} - -podDisruptionBudget: - # Enable or disable the PodDisruptionBudget resource. - # - # This prevents downtime during voluntary disruptions such as during a Node upgrade. - # For example, the PodDisruptionBudget will block `kubectl drain` - # if it is used on the Node where the only remaining cert-manager - # Pod is currently running. - enabled: false - - # This configures the minimum available pods for disruptions. It can either be set to - # an integer (e.g. 1) or a percentage value (e.g. 25%). - # It cannot be used if `maxUnavailable` is set. - # +docs:property - # +docs:type=unknown - # minAvailable: 1 - - # This configures the maximum unavailable pods for disruptions. It can either be set to - # an integer (e.g. 1) or a percentage value (e.g. 25%). - # it cannot be used if `minAvailable` is set. - # +docs:property - # +docs:type=unknown - # maxUnavailable: 1 - -# A comma-separated list of feature gates that should be enabled on the -# controller pod. -featureGates: "" - -# The maximum number of challenges that can be scheduled as 'processing' at once. -maxConcurrentChallenges: 60 - -image: - # The container registry to pull the manager image from. - # +docs:property - # registry: quay.io - - # The container image for the cert-manager controller. - # +docs:property - repository: quay.io/jetstack/cert-manager-controller - - # Override the image tag to deploy by setting this variable. - # If no value is set, the chart's appVersion is used. - # +docs:property - # tag: vX.Y.Z - - # Setting a digest will override any tag. - # +docs:property - # digest: sha256:0e072dddd1f7f8fc8909a2ca6f65e76c5f0d2fcfb8be47935ae3457e8bbceb20 - - # Kubernetes imagePullPolicy on Deployment. - pullPolicy: IfNotPresent - -# Override the namespace used to store DNS provider credentials etc. for ClusterIssuer -# resources. By default, the same namespace as cert-manager is deployed within is -# used. This namespace will not be automatically created by the Helm chart. -clusterResourceNamespace: "" - -# This namespace allows you to define where the services are installed into. -# If not set then they use the namespace of the release. -# This is helpful when installing cert manager as a chart dependency (sub chart). -namespace: "" - -# Override the "cert-manager.fullname" value. This value is used as part of -# most of the names of the resources created by this Helm chart. -# +docs:property -# fullnameOverride: "my-cert-manager" - -# Override the "cert-manager.name" value, which is used to annotate some of -# the resources that are created by this Chart (using "app.kubernetes.io/name"). -# NOTE: There are some inconsistencies in the Helm chart when it comes to -# these annotations (some resources use eg. "cainjector.name" which resolves -# to the value "cainjector"). -# +docs:property -# nameOverride: "my-cert-manager" - -serviceAccount: - # Specifies whether a service account should be created. - create: true - - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template. - # +docs:property - # name: "" - - # Optional additional annotations to add to the controller's Service Account. - # +docs:property - # annotations: {} - - # Optional additional labels to add to the controller's Service Account. - # +docs:property - # labels: {} - - # Automount API credentials for a Service Account. - automountServiceAccountToken: true - -# Automounting API credentials for a particular pod. -# +docs:property -# automountServiceAccountToken: true - -# When this flag is enabled, secrets will be automatically removed when the certificate resource is deleted. -enableCertificateOwnerRef: false - -# This property is used to configure options for the controller pod. -# This allows setting options that would usually be provided using flags. -# -# If `apiVersion` and `kind` are unspecified they default to the current latest -# version (currently `controller.config.cert-manager.io/v1alpha1`). You can pin -# the version by specifying the `apiVersion` yourself. -# -# For example: -# config: -# apiVersion: controller.config.cert-manager.io/v1alpha1 -# kind: ControllerConfiguration -# logging: -# verbosity: 2 -# format: text -# leaderElectionConfig: -# namespace: kube-system -# kubernetesAPIQPS: 9000 -# kubernetesAPIBurst: 9000 -# numberOfConcurrentWorkers: 200 -# featureGates: -# AdditionalCertificateOutputFormats: true -# DisallowInsecureCSRUsageDefinition: true -# ExperimentalCertificateSigningRequestControllers: true -# ExperimentalGatewayAPISupport: true -# LiteralCertificateSubject: true -# SecretsFilteredCaching: true -# ServerSideApply: true -# StableCertificateRequestName: true -# UseCertificateRequestBasicConstraints: true -# ValidateCAA: true -# # Configure the metrics server for TLS -# # See https://cert-manager.io/docs/devops-tips/prometheus-metrics/#tls -# metricsTLSConfig: -# dynamic: -# secretNamespace: "cert-manager" -# secretName: "cert-manager-metrics-ca" -# dnsNames: -# - cert-manager-metrics -config: {} - -# Setting Nameservers for DNS01 Self Check. -# For more information, see the [cert-manager documentation](https://cert-manager.io/docs/configuration/acme/dns01/#setting-nameservers-for-dns01-self-check). - -# A comma-separated string with the host and port of the recursive nameservers cert-manager should query. -dns01RecursiveNameservers: "" - -# Forces cert-manager to use only the recursive nameservers for verification. -# Enabling this option could cause the DNS01 self check to take longer owing to caching performed by the recursive nameservers. -dns01RecursiveNameserversOnly: false - -# Option to disable cert-manager's build-in auto-approver. The auto-approver -# approves all CertificateRequests that reference issuers matching the 'approveSignerNames' -# option. This 'disableAutoApproval' option is useful when you want to make all approval decisions -# using a different approver (like approver-policy - https://github.com/cert-manager/approver-policy). -disableAutoApproval: false - -# List of signer names that cert-manager will approve by default. CertificateRequests -# referencing these signer names will be auto-approved by cert-manager. Defaults to just -# approving the cert-manager.io Issuer and ClusterIssuer issuers. When set to an empty -# array, ALL issuers will be auto-approved by cert-manager. To disable the auto-approval, -# because eg. you are using approver-policy, you can enable 'disableAutoApproval'. -# ref: https://cert-manager.io/docs/concepts/certificaterequest/#approval -# +docs:property -approveSignerNames: -- issuers.cert-manager.io/* -- clusterissuers.cert-manager.io/* - -# Additional command line flags to pass to cert-manager controller binary. -# To see all available flags run `docker run quay.io/jetstack/cert-manager-controller: --help`. -# -# Use this flag to enable or disable arbitrary controllers. For example, to disable the CertificateRequests approver. -# -# For example: -# extraArgs: -# - --controllers=*,-certificaterequests-approver -extraArgs: [] - -# Additional environment variables to pass to cert-manager controller binary. -# For example: -# extraEnv: -# - name: SOME_VAR -# value: 'some value' -extraEnv: [] - -# Resources to provide to the cert-manager controller pod. -# -# For example: -# requests: -# cpu: 10m -# memory: 32Mi -# -# For more information, see [Resource Management for Pods and Containers](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/). -resources: {} - -# Pod Security Context. -# For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/). -# +docs:property -securityContext: - runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - -# Container Security Context to be set on the controller component container. -# For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/). -# +docs:property -containerSecurityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - readOnlyRootFilesystem: true - -# Additional volumes to add to the cert-manager controller pod. -volumes: [] - -# Additional volume mounts to add to the cert-manager controller container. -volumeMounts: [] - -# Optional additional annotations to add to the controller Deployment. -# +docs:property -# deploymentAnnotations: {} - -# Optional additional annotations to add to the controller Pods. -# +docs:property -# podAnnotations: {} - -# Optional additional labels to add to the controller Pods. -podLabels: {} - -# Optional annotations to add to the controller Service. -# +docs:property -# serviceAnnotations: {} - -# Optional additional labels to add to the controller Service. -# +docs:property -# serviceLabels: {} - -# Optionally set the IP family policy for the controller Service to configure dual-stack; see [Configure dual-stack](https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services). -# +docs:property -# serviceIPFamilyPolicy: "" - -# Optionally set the IP families for the controller Service that should be supported, in the order in which they should be applied to ClusterIP. Can be IPv4 and/or IPv6. -# +docs:property -# serviceIPFamilies: [] - -# Optional DNS settings. These are useful if you have a public and private DNS zone for -# the same domain on Route 53. The following is an example of ensuring -# cert-manager can access an ingress or DNS TXT records at all times. -# Note that this requires Kubernetes 1.10 or `CustomPodDNS` feature gate enabled for -# the cluster to work. - -# Pod DNS policy. -# For more information, see [Pod's DNS Policy](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy). -# +docs:property -# podDnsPolicy: "None" - -# Pod DNS configuration. The podDnsConfig field is optional and can work with any podDnsPolicy -# settings. However, when a Pod's dnsPolicy is set to "None", the dnsConfig field has to be specified. -# For more information, see [Pod's DNS Config](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-dns-config). -# +docs:property -# podDnsConfig: -# nameservers: -# - "1.1.1.1" -# - "8.8.8.8" - -# Optional hostAliases for cert-manager-controller pods. May be useful when performing ACME DNS-01 self checks. -hostAliases: [] -# - ip: 127.0.0.1 -# hostnames: -# - foo.local -# - bar.local -# - ip: 10.1.2.3 -# hostnames: -# - foo.remote -# - bar.remote - -# The nodeSelector on Pods tells Kubernetes to schedule Pods on the nodes with -# matching labels. -# For more information, see [Assigning Pods to Nodes](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). -# -# This default ensures that Pods are only scheduled to Linux nodes. -# It prevents Pods being scheduled to Windows nodes in a mixed OS cluster. -# +docs:property -nodeSelector: - kubernetes.io/os: linux - -# +docs:ignore -ingressShim: {} - - # Optional default issuer to use for ingress resources. - # +docs:property=ingressShim.defaultIssuerName - # defaultIssuerName: "" - - # Optional default issuer kind to use for ingress resources. - # +docs:property=ingressShim.defaultIssuerKind - # defaultIssuerKind: "" - - # Optional default issuer group to use for ingress resources. - # +docs:property=ingressShim.defaultIssuerGroup - # defaultIssuerGroup: "" - -# Use these variables to configure the HTTP_PROXY environment variables. - -# Configures the HTTP_PROXY environment variable where a HTTP proxy is required. -# +docs:property -# http_proxy: "http://proxy:8080" - -# Configures the HTTPS_PROXY environment variable where a HTTP proxy is required. -# +docs:property -# https_proxy: "https://proxy:8080" - -# Configures the NO_PROXY environment variable where a HTTP proxy is required, -# but certain domains should be excluded. -# +docs:property -# no_proxy: 127.0.0.1,localhost - - -# A Kubernetes Affinity, if required. For more information, see [Affinity v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#affinity-v1-core). -# -# For example: -# affinity: -# nodeAffinity: -# requiredDuringSchedulingIgnoredDuringExecution: -# nodeSelectorTerms: -# - matchExpressions: -# - key: foo.bar.com/role -# operator: In -# values: -# - master -affinity: {} - -# A list of Kubernetes Tolerations, if required. For more information, see [Toleration v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#toleration-v1-core). -# -# For example: -# tolerations: -# - key: foo.bar.com/role -# operator: Equal -# value: master -# effect: NoSchedule -tolerations: [] - -# A list of Kubernetes TopologySpreadConstraints, if required. For more information, see [Topology spread constraint v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#topologyspreadconstraint-v1-core -# -# For example: -# topologySpreadConstraints: -# - maxSkew: 2 -# topologyKey: topology.kubernetes.io/zone -# whenUnsatisfiable: ScheduleAnyway -# labelSelector: -# matchLabels: -# app.kubernetes.io/instance: cert-manager -# app.kubernetes.io/component: controller -topologySpreadConstraints: [] - -# LivenessProbe settings for the controller container of the controller Pod. -# -# This is enabled by default, in order to enable the clock-skew liveness probe that -# restarts the controller in case of a skew between the system clock and the monotonic clock. -# LivenessProbe durations and thresholds are based on those used for the Kubernetes -# controller-manager. For more information see the following on the -# [Kubernetes GitHub repository](https://github.com/kubernetes/kubernetes/blob/806b30170c61a38fedd54cc9ede4cd6275a1ad3b/cmd/kubeadm/app/util/staticpod/utils.go#L241-L245) -# +docs:property -livenessProbe: - enabled: true - initialDelaySeconds: 10 - periodSeconds: 10 - timeoutSeconds: 15 - successThreshold: 1 - failureThreshold: 8 - -# enableServiceLinks indicates whether information about services should be -# injected into the pod's environment variables, matching the syntax of Docker -# links. -enableServiceLinks: false - -# +docs:section=Prometheus - -prometheus: - # Enable Prometheus monitoring for the cert-manager controller and webhook. - # If you use the Prometheus Operator, set prometheus.podmonitor.enabled or - # prometheus.servicemonitor.enabled, to create a PodMonitor or a - # ServiceMonitor resource. - # Otherwise, 'prometheus.io' annotations are added to the cert-manager and - # cert-manager-webhook Deployments. - # Note that you can not enable both PodMonitor and ServiceMonitor as they are - # mutually exclusive. Enabling both will result in an error. - enabled: true - - servicemonitor: - # Create a ServiceMonitor to add cert-manager to Prometheus. - enabled: false - - # The namespace that the service monitor should live in, defaults - # to the cert-manager namespace. - # +docs:property - # namespace: cert-manager - - # Specifies the `prometheus` label on the created ServiceMonitor. This is - # used when different Prometheus instances have label selectors matching - # different ServiceMonitors. - prometheusInstance: default - - # The target port to set on the ServiceMonitor. This must match the port that the - # cert-manager controller is listening on for metrics. - targetPort: 9402 - - # The path to scrape for metrics. - path: /metrics - - # The interval to scrape metrics. - interval: 60s - - # The timeout before a metrics scrape fails. - scrapeTimeout: 30s - - # Additional labels to add to the ServiceMonitor. - labels: {} - - # Additional annotations to add to the ServiceMonitor. - annotations: {} - - # Keep labels from scraped data, overriding server-side labels. - honorLabels: false - - # EndpointAdditionalProperties allows setting additional properties on the - # endpoint such as relabelings, metricRelabelings etc. - # - # For example: - # endpointAdditionalProperties: - # relabelings: - # - action: replace - # sourceLabels: - # - __meta_kubernetes_pod_node_name - # targetLabel: instance - # - # +docs:property - endpointAdditionalProperties: {} - - # Note that you can not enable both PodMonitor and ServiceMonitor as they are mutually exclusive. Enabling both will result in an error. - podmonitor: - # Create a PodMonitor to add cert-manager to Prometheus. - enabled: false - - # The namespace that the pod monitor should live in, defaults - # to the cert-manager namespace. - # +docs:property - # namespace: cert-manager - - # Specifies the `prometheus` label on the created PodMonitor. This is - # used when different Prometheus instances have label selectors matching - # different PodMonitors. - prometheusInstance: default - - # The path to scrape for metrics. - path: /metrics - - # The interval to scrape metrics. - interval: 60s - - # The timeout before a metrics scrape fails. - scrapeTimeout: 30s - - # Additional labels to add to the PodMonitor. - labels: {} - - # Additional annotations to add to the PodMonitor. - annotations: {} - - # Keep labels from scraped data, overriding server-side labels. - honorLabels: false - - # EndpointAdditionalProperties allows setting additional properties on the - # endpoint such as relabelings, metricRelabelings etc. - # - # For example: - # endpointAdditionalProperties: - # relabelings: - # - action: replace - # sourceLabels: - # - __meta_kubernetes_pod_node_name - # targetLabel: instance - # # Configure the PodMonitor for TLS connections - # # See https://cert-manager.io/docs/devops-tips/prometheus-metrics/#tls - # scheme: https - # tlsConfig: - # serverName: cert-manager-metrics - # ca: - # secret: - # name: cert-manager-metrics-ca - # key: "tls.crt" - # - # +docs:property - endpointAdditionalProperties: {} - -# +docs:section=Webhook - -webhook: - # Number of replicas of the cert-manager webhook to run. - # - # The default is 1, but in production set this to 2 or 3 to provide high - # availability. - # - # If `replicas > 1`, consider setting `webhook.podDisruptionBudget.enabled=true`. - replicaCount: 1 - - # The number of seconds the API server should wait for the webhook to respond before treating the call as a failure. - # The value must be between 1 and 30 seconds. For more information, see - # [Validating webhook configuration v1](https://kubernetes.io/docs/reference/kubernetes-api/extend-resources/validating-webhook-configuration-v1/). - # - # The default is set to the maximum value of 30 seconds as - # users sometimes report that the connection between the K8S API server and - # the cert-manager webhook server times out. - # If *this* timeout is reached, the error message will be "context deadline exceeded", - # which doesn't help the user diagnose what phase of the HTTPS connection timed out. - # For example, it could be during DNS resolution, TCP connection, TLS - # negotiation, HTTP negotiation, or slow HTTP response from the webhook - # server. - # By setting this timeout to its maximum value the underlying timeout error - # message has more chance of being returned to the end user. - timeoutSeconds: 30 - - # This is used to configure options for the webhook pod. - # This allows setting options that would usually be provided using flags. - # - # If `apiVersion` and `kind` are unspecified they default to the current latest - # version (currently `webhook.config.cert-manager.io/v1alpha1`). You can pin - # the version by specifying the `apiVersion` yourself. - # - # For example: - # apiVersion: webhook.config.cert-manager.io/v1alpha1 - # kind: WebhookConfiguration - # # The port that the webhook listens on for requests. - # # In GKE private clusters, by default Kubernetes apiservers are allowed to - # # talk to the cluster nodes only on 443 and 10250. Configuring - # # securePort: 10250 therefore will work out-of-the-box without needing to add firewall - # # rules or requiring NET_BIND_SERVICE capabilities to bind port numbers < 1000. - # # This should be uncommented and set as a default by the chart once - # # the apiVersion of WebhookConfiguration graduates beyond v1alpha1. - # securePort: 10250 - # # Configure the metrics server for TLS - # # See https://cert-manager.io/docs/devops-tips/prometheus-metrics/#tls - # metricsTLSConfig: - # dynamic: - # secretNamespace: "cert-manager" - # secretName: "cert-manager-metrics-ca" - # dnsNames: - # - cert-manager-metrics - config: {} - - # The update strategy for the cert-manager webhook deployment. - # For more information, see the [Kubernetes documentation](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy) - # - # For example: - # strategy: - # type: RollingUpdate - # rollingUpdate: - # maxSurge: 0 - # maxUnavailable: 1 - strategy: {} - - # Pod Security Context to be set on the webhook component Pod. - # For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/). - # +docs:property - securityContext: - runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - - # Container Security Context to be set on the webhook component container. - # For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/). - # +docs:property - containerSecurityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - readOnlyRootFilesystem: true - - podDisruptionBudget: - # Enable or disable the PodDisruptionBudget resource. - # - # This prevents downtime during voluntary disruptions such as during a Node upgrade. - # For example, the PodDisruptionBudget will block `kubectl drain` - # if it is used on the Node where the only remaining cert-manager - # Pod is currently running. - enabled: false - - # This property configures the minimum available pods for disruptions. Can either be set to - # an integer (e.g. 1) or a percentage value (e.g. 25%). - # It cannot be used if `maxUnavailable` is set. - # +docs:property - # +docs:type=unknown - # minAvailable: 1 - - # This property configures the maximum unavailable pods for disruptions. Can either be set to - # an integer (e.g. 1) or a percentage value (e.g. 25%). - # It cannot be used if `minAvailable` is set. - # +docs:property - # +docs:type=unknown - # maxUnavailable: 1 - - # Optional additional annotations to add to the webhook Deployment. - # +docs:property - # deploymentAnnotations: {} - - # Optional additional annotations to add to the webhook Pods. - # +docs:property - # podAnnotations: {} - - # Optional additional annotations to add to the webhook Service. - # +docs:property - # serviceAnnotations: {} - - # Optional additional annotations to add to the webhook MutatingWebhookConfiguration. - # +docs:property - # mutatingWebhookConfigurationAnnotations: {} - - # Optional additional annotations to add to the webhook ValidatingWebhookConfiguration. - # +docs:property - # validatingWebhookConfigurationAnnotations: {} - - validatingWebhookConfiguration: - # Configure spec.namespaceSelector for validating webhooks. - # +docs:property - namespaceSelector: - matchExpressions: - - key: "cert-manager.io/disable-validation" - operator: "NotIn" - values: - - "true" - - mutatingWebhookConfiguration: - # Configure spec.namespaceSelector for mutating webhooks. - # +docs:property - namespaceSelector: {} - # matchLabels: - # key: value - # matchExpressions: - # - key: kubernetes.io/metadata.name - # operator: NotIn - # values: - # - kube-system - - - # Additional command line flags to pass to cert-manager webhook binary. - # To see all available flags run `docker run quay.io/jetstack/cert-manager-webhook: --help`. - extraArgs: [] - # Path to a file containing a WebhookConfiguration object used to configure the webhook. - # - --config= - - # Additional environment variables to pass to cert-manager webhook binary. - # For example: - # extraEnv: - # - name: SOME_VAR - # value: 'some value' - extraEnv: [] - - # Comma separated list of feature gates that should be enabled on the - # webhook pod. - featureGates: "" - - # Resources to provide to the cert-manager webhook pod. - # - # For example: - # requests: - # cpu: 10m - # memory: 32Mi - # - # For more information, see [Resource Management for Pods and Containers](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/). - resources: {} - - # Liveness probe values. - # For more information, see [Container probes](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes). - # - # +docs:property - livenessProbe: - failureThreshold: 3 - initialDelaySeconds: 60 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 - - # Readiness probe values. - # For more information, see [Container probes](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes). - # - # +docs:property - readinessProbe: - failureThreshold: 3 - initialDelaySeconds: 5 - periodSeconds: 5 - successThreshold: 1 - timeoutSeconds: 1 - - # The nodeSelector on Pods tells Kubernetes to schedule Pods on the nodes with - # matching labels. - # For more information, see [Assigning Pods to Nodes](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). - # - # This default ensures that Pods are only scheduled to Linux nodes. - # It prevents Pods being scheduled to Windows nodes in a mixed OS cluster. - # +docs:property - nodeSelector: - kubernetes.io/os: linux - - # A Kubernetes Affinity, if required. For more information, see [Affinity v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#affinity-v1-core). - # - # For example: - # affinity: - # nodeAffinity: - # requiredDuringSchedulingIgnoredDuringExecution: - # nodeSelectorTerms: - # - matchExpressions: - # - key: foo.bar.com/role - # operator: In - # values: - # - master - affinity: {} - - # A list of Kubernetes Tolerations, if required. For more information, see [Toleration v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#toleration-v1-core). - # - # For example: - # tolerations: - # - key: foo.bar.com/role - # operator: Equal - # value: master - # effect: NoSchedule - tolerations: [] - - # A list of Kubernetes TopologySpreadConstraints, if required. For more information, see [Topology spread constraint v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#topologyspreadconstraint-v1-core). - # - # For example: - # topologySpreadConstraints: - # - maxSkew: 2 - # topologyKey: topology.kubernetes.io/zone - # whenUnsatisfiable: ScheduleAnyway - # labelSelector: - # matchLabels: - # app.kubernetes.io/instance: cert-manager - # app.kubernetes.io/component: controller - topologySpreadConstraints: [] - - # Optional additional labels to add to the Webhook Pods. - podLabels: {} - - # Optional additional labels to add to the Webhook Service. - serviceLabels: {} - - # Optionally set the IP family policy for the controller Service to configure dual-stack; see [Configure dual-stack](https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services). - serviceIPFamilyPolicy: "" - - # Optionally set the IP families for the controller Service that should be supported, in the order in which they should be applied to ClusterIP. Can be IPv4 and/or IPv6. - serviceIPFamilies: [] - - image: - # The container registry to pull the webhook image from. - # +docs:property - # registry: quay.io - - # The container image for the cert-manager webhook - # +docs:property - repository: quay.io/jetstack/cert-manager-webhook - - # Override the image tag to deploy by setting this variable. - # If no value is set, the chart's appVersion will be used. - # +docs:property - # tag: vX.Y.Z - - # Setting a digest will override any tag - # +docs:property - # digest: sha256:0e072dddd1f7f8fc8909a2ca6f65e76c5f0d2fcfb8be47935ae3457e8bbceb20 - - # Kubernetes imagePullPolicy on Deployment. - pullPolicy: IfNotPresent - - serviceAccount: - # Specifies whether a service account should be created. - create: true - - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template. - # +docs:property - # name: "" - - # Optional additional annotations to add to the webhook's Service Account. - # +docs:property - # annotations: {} - - # Optional additional labels to add to the webhook's Service Account. - # +docs:property - # labels: {} - - # Automount API credentials for a Service Account. - automountServiceAccountToken: true - - # Automounting API credentials for a particular pod. - # +docs:property - # automountServiceAccountToken: true - - # The port that the webhook listens on for requests. - # In GKE private clusters, by default Kubernetes apiservers are allowed to - # talk to the cluster nodes only on 443 and 10250. Configuring - # securePort: 10250, therefore will work out-of-the-box without needing to add firewall - # rules or requiring NET_BIND_SERVICE capabilities to bind port numbers <1000. - securePort: 10250 - - # Specifies if the webhook should be started in hostNetwork mode. - # - # Required for use in some managed kubernetes clusters (such as AWS EKS) with custom - # CNI (such as calico), because control-plane managed by AWS cannot communicate - # with pods' IP CIDR and admission webhooks are not working - # - # Since the default port for the webhook conflicts with kubelet on the host - # network, `webhook.securePort` should be changed to an available port if - # running in hostNetwork mode. - hostNetwork: false - - # Specifies how the service should be handled. Useful if you want to expose the - # webhook outside of the cluster. In some cases, the control plane cannot - # reach internal services. - serviceType: ClusterIP - - # Specify the load balancer IP for the created service. - # +docs:property - # loadBalancerIP: "10.10.10.10" - - # Overrides the mutating webhook and validating webhook so they reach the webhook - # service using the `url` field instead of a service. - url: {} - # host: - - # Enables default network policies for webhooks. - networkPolicy: - # Create network policies for the webhooks. - enabled: false - - # Ingress rule for the webhook network policy. By default, it allows all - # inbound traffic. - # +docs:property - ingress: - - from: - - ipBlock: - cidr: 0.0.0.0/0 - - # Egress rule for the webhook network policy. By default, it allows all - # outbound traffic to ports 80 and 443, as well as DNS ports. - # +docs:property - egress: - - ports: - - port: 80 - protocol: TCP - - port: 443 - protocol: TCP - - port: 53 - protocol: TCP - - port: 53 - protocol: UDP - # On OpenShift and OKD, the Kubernetes API server listens on. - # port 6443. - - port: 6443 - protocol: TCP - to: - - ipBlock: - cidr: 0.0.0.0/0 - - # Additional volumes to add to the cert-manager controller pod. - volumes: [] - - # Additional volume mounts to add to the cert-manager controller container. - volumeMounts: [] - - # enableServiceLinks indicates whether information about services should be - # injected into the pod's environment variables, matching the syntax of Docker - # links. - enableServiceLinks: false - -# +docs:section=CA Injector - -cainjector: - # Create the CA Injector deployment - enabled: true - - # The number of replicas of the cert-manager cainjector to run. - # - # The default is 1, but in production set this to 2 or 3 to provide high - # availability. - # - # If `replicas > 1`, consider setting `cainjector.podDisruptionBudget.enabled=true`. - # - # Note that cert-manager uses leader election to ensure that there can - # only be a single instance active at a time. - replicaCount: 1 - - # This is used to configure options for the cainjector pod. - # It allows setting options that are usually provided via flags. - # - # If `apiVersion` and `kind` are unspecified they default to the current latest - # version (currently `cainjector.config.cert-manager.io/v1alpha1`). You can pin - # the version by specifying the `apiVersion` yourself. - # - # For example: - # apiVersion: cainjector.config.cert-manager.io/v1alpha1 - # kind: CAInjectorConfiguration - # logging: - # verbosity: 2 - # format: text - # leaderElectionConfig: - # namespace: kube-system - # # Configure the metrics server for TLS - # # See https://cert-manager.io/docs/devops-tips/prometheus-metrics/#tls - # metricsTLSConfig: - # dynamic: - # secretNamespace: "cert-manager" - # secretName: "cert-manager-metrics-ca" - # dnsNames: - # - cert-manager-metrics - config: {} - - # Deployment update strategy for the cert-manager cainjector deployment. - # For more information, see the [Kubernetes documentation](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy). - # - # For example: - # strategy: - # type: RollingUpdate - # rollingUpdate: - # maxSurge: 0 - # maxUnavailable: 1 - strategy: {} - - # Pod Security Context to be set on the cainjector component Pod - # For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/). - # +docs:property - securityContext: - runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - - # Container Security Context to be set on the cainjector component container - # For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/). - # +docs:property - containerSecurityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - readOnlyRootFilesystem: true - - podDisruptionBudget: - # Enable or disable the PodDisruptionBudget resource. - # - # This prevents downtime during voluntary disruptions such as during a Node upgrade. - # For example, the PodDisruptionBudget will block `kubectl drain` - # if it is used on the Node where the only remaining cert-manager - # Pod is currently running. - enabled: false - - # `minAvailable` configures the minimum available pods for disruptions. It can either be set to - # an integer (e.g. 1) or a percentage value (e.g. 25%). - # Cannot be used if `maxUnavailable` is set. - # +docs:property - # +docs:type=unknown - # minAvailable: 1 - - # `maxUnavailable` configures the maximum unavailable pods for disruptions. It can either be set to - # an integer (e.g. 1) or a percentage value (e.g. 25%). - # Cannot be used if `minAvailable` is set. - # +docs:property - # +docs:type=unknown - # maxUnavailable: 1 - - # Optional additional annotations to add to the cainjector Deployment. - # +docs:property - # deploymentAnnotations: {} - - # Optional additional annotations to add to the cainjector Pods. - # +docs:property - # podAnnotations: {} - - # Optional additional annotations to add to the cainjector metrics Service. - # +docs:property - # serviceAnnotations: {} - - # Additional command line flags to pass to cert-manager cainjector binary. - # To see all available flags run `docker run quay.io/jetstack/cert-manager-cainjector: --help`. - extraArgs: [] - # Enable profiling for cainjector. - # - --enable-profiling=true - - # Additional environment variables to pass to cert-manager cainjector binary. - # For example: - # extraEnv: - # - name: SOME_VAR - # value: 'some value' - extraEnv: [] - - # Comma separated list of feature gates that should be enabled on the - # cainjector pod. - featureGates: "" - - # Resources to provide to the cert-manager cainjector pod. - # - # For example: - # requests: - # cpu: 10m - # memory: 32Mi - # - # For more information, see [Resource Management for Pods and Containers](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/). - resources: {} - - - # The nodeSelector on Pods tells Kubernetes to schedule Pods on the nodes with - # matching labels. - # For more information, see [Assigning Pods to Nodes](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). - # - # This default ensures that Pods are only scheduled to Linux nodes. - # It prevents Pods being scheduled to Windows nodes in a mixed OS cluster. - # +docs:property - nodeSelector: - kubernetes.io/os: linux - - # A Kubernetes Affinity, if required. For more information, see [Affinity v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#affinity-v1-core). - # - # For example: - # affinity: - # nodeAffinity: - # requiredDuringSchedulingIgnoredDuringExecution: - # nodeSelectorTerms: - # - matchExpressions: - # - key: foo.bar.com/role - # operator: In - # values: - # - master - affinity: {} - - # A list of Kubernetes Tolerations, if required. For more information, see [Toleration v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#toleration-v1-core). - # - # For example: - # tolerations: - # - key: foo.bar.com/role - # operator: Equal - # value: master - # effect: NoSchedule - tolerations: [] - - # A list of Kubernetes TopologySpreadConstraints, if required. For more information, see [Topology spread constraint v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#topologyspreadconstraint-v1-core). - # - # For example: - # topologySpreadConstraints: - # - maxSkew: 2 - # topologyKey: topology.kubernetes.io/zone - # whenUnsatisfiable: ScheduleAnyway - # labelSelector: - # matchLabels: - # app.kubernetes.io/instance: cert-manager - # app.kubernetes.io/component: controller - topologySpreadConstraints: [] - - # Optional additional labels to add to the CA Injector Pods. - podLabels: {} - - # Optional additional labels to add to the CA Injector metrics Service. - serviceLabels: {} - - image: - # The container registry to pull the cainjector image from. - # +docs:property - # registry: quay.io - - # The container image for the cert-manager cainjector - # +docs:property - repository: quay.io/jetstack/cert-manager-cainjector - - # Override the image tag to deploy by setting this variable. - # If no value is set, the chart's appVersion will be used. - # +docs:property - # tag: vX.Y.Z - - # Setting a digest will override any tag. - # +docs:property - # digest: sha256:0e072dddd1f7f8fc8909a2ca6f65e76c5f0d2fcfb8be47935ae3457e8bbceb20 - - # Kubernetes imagePullPolicy on Deployment. - pullPolicy: IfNotPresent - - serviceAccount: - # Specifies whether a service account should be created. - create: true - - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template - # +docs:property - # name: "" - - # Optional additional annotations to add to the cainjector's Service Account. - # +docs:property - # annotations: {} - - # Optional additional labels to add to the cainjector's Service Account. - # +docs:property - # labels: {} - - # Automount API credentials for a Service Account. - automountServiceAccountToken: true - - # Automounting API credentials for a particular pod. - # +docs:property - # automountServiceAccountToken: true - - # Additional volumes to add to the cert-manager controller pod. - volumes: [] - - # Additional volume mounts to add to the cert-manager controller container. - volumeMounts: [] - - # enableServiceLinks indicates whether information about services should be - # injected into the pod's environment variables, matching the syntax of Docker - # links. - enableServiceLinks: false - -# +docs:section=ACME Solver - -acmesolver: - image: - # The container registry to pull the acmesolver image from. - # +docs:property - # registry: quay.io - - # The container image for the cert-manager acmesolver. - # +docs:property - repository: quay.io/jetstack/cert-manager-acmesolver - - # Override the image tag to deploy by setting this variable. - # If no value is set, the chart's appVersion is used. - # +docs:property - # tag: vX.Y.Z - - # Setting a digest will override any tag. - # +docs:property - # digest: sha256:0e072dddd1f7f8fc8909a2ca6f65e76c5f0d2fcfb8be47935ae3457e8bbceb20 - - # Kubernetes imagePullPolicy on Deployment. - pullPolicy: IfNotPresent - -# +docs:section=Startup API Check -# This startupapicheck is a Helm post-install hook that waits for the webhook -# endpoints to become available. -# The check is implemented using a Kubernetes Job - if you are injecting mesh -# sidecar proxies into cert-manager pods, ensure that they -# are not injected into this Job's pod. Otherwise, the installation may time out -# owing to the Job never being completed because the sidecar proxy does not exit. -# For more information, see [this note](https://github.com/cert-manager/cert-manager/pull/4414). - -startupapicheck: - # Enables the startup api check. - enabled: true - - # Pod Security Context to be set on the startupapicheck component Pod. - # For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/). - # +docs:property - securityContext: - runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - - # Container Security Context to be set on the controller component container. - # For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/). - # +docs:property - containerSecurityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - readOnlyRootFilesystem: true - - # Timeout for 'kubectl check api' command. - timeout: 1m - - # Job backoffLimit - backoffLimit: 4 - - # Optional additional annotations to add to the startupapicheck Job. - # +docs:property - jobAnnotations: - helm.sh/hook: post-install - helm.sh/hook-weight: "1" - helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded - - # Optional additional annotations to add to the startupapicheck Pods. - # +docs:property - # podAnnotations: {} - - # Additional command line flags to pass to startupapicheck binary. - # To see all available flags run `docker run quay.io/jetstack/cert-manager-startupapicheck: --help`. - # - # Verbose logging is enabled by default so that if startupapicheck fails, you - # can know what exactly caused the failure. Verbose logs include details of - # the webhook URL, IP address and TCP connect errors for example. - # +docs:property - extraArgs: - - -v - - # Additional environment variables to pass to cert-manager startupapicheck binary. - # For example: - # extraEnv: - # - name: SOME_VAR - # value: 'some value' - extraEnv: [] - - # Resources to provide to the cert-manager controller pod. - # - # For example: - # requests: - # cpu: 10m - # memory: 32Mi - # - # For more information, see [Resource Management for Pods and Containers](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/). - resources: {} - - - # The nodeSelector on Pods tells Kubernetes to schedule Pods on the nodes with - # matching labels. - # For more information, see [Assigning Pods to Nodes](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). - # - # This default ensures that Pods are only scheduled to Linux nodes. - # It prevents Pods being scheduled to Windows nodes in a mixed OS cluster. - # +docs:property - nodeSelector: - kubernetes.io/os: linux - - # A Kubernetes Affinity, if required. For more information, see [Affinity v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#affinity-v1-core). - # For example: - # affinity: - # nodeAffinity: - # requiredDuringSchedulingIgnoredDuringExecution: - # nodeSelectorTerms: - # - matchExpressions: - # - key: foo.bar.com/role - # operator: In - # values: - # - master - affinity: {} - - # A list of Kubernetes Tolerations, if required. For more information, see [Toleration v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#toleration-v1-core). - # - # For example: - # tolerations: - # - key: foo.bar.com/role - # operator: Equal - # value: master - # effect: NoSchedule - tolerations: [] - - # Optional additional labels to add to the startupapicheck Pods. - podLabels: {} - - image: - # The container registry to pull the startupapicheck image from. - # +docs:property - # registry: quay.io - - # The container image for the cert-manager startupapicheck. - # +docs:property - repository: quay.io/jetstack/cert-manager-startupapicheck - - # Override the image tag to deploy by setting this variable. - # If no value is set, the chart's appVersion is used. - # +docs:property - # tag: vX.Y.Z - - # Setting a digest will override any tag. - # +docs:property - # digest: sha256:0e072dddd1f7f8fc8909a2ca6f65e76c5f0d2fcfb8be47935ae3457e8bbceb20 - - # Kubernetes imagePullPolicy on Deployment. - pullPolicy: IfNotPresent - - rbac: - # annotations for the startup API Check job RBAC and PSP resources. - # +docs:property - annotations: - helm.sh/hook: post-install - helm.sh/hook-weight: "-5" - helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded - - # Automounting API credentials for a particular pod. - # +docs:property - # automountServiceAccountToken: true - - serviceAccount: - # Specifies whether a service account should be created. - create: true - - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template. - # +docs:property - # name: "" - - # Optional additional annotations to add to the Job's Service Account. - # +docs:property - annotations: - helm.sh/hook: post-install - helm.sh/hook-weight: "-5" - helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded - - # Automount API credentials for a Service Account. - # +docs:property - automountServiceAccountToken: true - - # Optional additional labels to add to the startupapicheck's Service Account. - # +docs:property - # labels: {} - - # Additional volumes to add to the cert-manager controller pod. - volumes: [] - - # Additional volume mounts to add to the cert-manager controller container. - volumeMounts: [] - - # enableServiceLinks indicates whether information about services should be - # injected into pod's environment variables, matching the syntax of Docker - # links. - enableServiceLinks: false - -# Create dynamic manifests via values. -# -# For example: -# extraObjects: -# - | -# apiVersion: v1 -# kind: ConfigMap -# metadata: -# name: '{{ template "cert-manager.fullname" . }}-extra-configmap' -extraObjects: [] - -# Field used by our release pipeline to produce the static manifests. -# The field defaults to "helm" but is set to "static" when we render -# the static YAML manifests. -# +docs:hidden -creator: "helm" - -# Field that can be used as a condition when cert-manager is a dependency. -# This definition is only here as a placeholder such that it is included in -# the json schema. -# See https://helm.sh/docs/chart_best_practices/dependencies/#conditions-and-tags -# for more info. -# +docs:hidden -enabled: true diff --git a/packages/system/cert-manager-crds/charts/cert-manager/templates/_helpers.tpl b/packages/system/cert-manager-crds/templates/_helpers.tpl similarity index 95% rename from packages/system/cert-manager-crds/charts/cert-manager/templates/_helpers.tpl rename to packages/system/cert-manager-crds/templates/_helpers.tpl index e15fa191..f85373f3 100644 --- a/packages/system/cert-manager-crds/charts/cert-manager/templates/_helpers.tpl +++ b/packages/system/cert-manager-crds/templates/_helpers.tpl @@ -187,6 +187,17 @@ See https://github.com/cert-manager/cert-manager/issues/6329 for a list of linke {{- end }} {{- end }} +{{/* +Labels for the CRD resources. +*/}} +{{- define "cert-manager.crd-labels" -}} +app: "{{ template "cert-manager.name" . }}" +app.kubernetes.io/name: "{{ template "cert-manager.name" . }}" +app.kubernetes.io/instance: "{{ .Release.Name }}" +app.kubernetes.io/component: "crds" +{{ include "labels" . }} +{{- end -}} + {{/* Check that the user has not set both .installCRDs and .crds.enabled or set .installCRDs and disabled .crds.keep. diff --git a/packages/system/cert-manager-crds/templates/crd-acme.cert-manager.io_challenges.yaml b/packages/system/cert-manager-crds/templates/crd-acme.cert-manager.io_challenges.yaml new file mode 100644 index 00000000..5bed0cd8 --- /dev/null +++ b/packages/system/cert-manager-crds/templates/crd-acme.cert-manager.io_challenges.yaml @@ -0,0 +1,3281 @@ +{{- if or .Values.crds.enabled .Values.installCRDs }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: "challenges.acme.cert-manager.io" + {{- if .Values.crds.keep }} + annotations: + helm.sh/resource-policy: keep + {{- end }} + labels: + {{- include "cert-manager.crd-labels" . | nindent 4 }} +spec: + group: acme.cert-manager.io + names: + categories: + - cert-manager + - cert-manager-acme + kind: Challenge + listKind: ChallengeList + plural: challenges + singular: challenge + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.state + name: State + type: string + - jsonPath: .spec.dnsName + name: Domain + type: string + - jsonPath: .status.reason + name: Reason + priority: 1 + type: string + - description: CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC. + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: Challenge is a type to represent a Challenge request with an ACME server + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + properties: + authorizationURL: + description: |- + The URL to the ACME Authorization resource that this + challenge is a part of. + type: string + dnsName: + description: |- + dnsName is the identifier that this challenge is for, e.g., example.com. + If the requested DNSName is a 'wildcard', this field MUST be set to the + non-wildcard domain, e.g., for `*.example.com`, it must be `example.com`. + type: string + issuerRef: + description: |- + References a properly configured ACME-type Issuer which should + be used to create this Challenge. + If the Issuer does not exist, processing will be retried. + If the Issuer is not an 'ACME' Issuer, an error will be returned and the + Challenge will be marked as failed. + properties: + group: + description: |- + Group of the issuer being referred to. + Defaults to 'cert-manager.io'. + type: string + kind: + description: |- + Kind of the issuer being referred to. + Defaults to 'Issuer'. + type: string + name: + description: Name of the issuer being referred to. + type: string + required: + - name + type: object + key: + description: |- + The ACME challenge key for this challenge + For HTTP01 challenges, this is the value that must be responded with to + complete the HTTP01 challenge in the format: + `.`. + For DNS01 challenges, this is the base64 encoded SHA256 sum of the + `.` + text that must be set as the TXT record content. + type: string + solver: + description: |- + Contains the domain solving configuration that should be used to + solve this challenge resource. + properties: + dns01: + description: |- + Configures cert-manager to attempt to complete authorizations by + performing the DNS01 challenge flow. + properties: + acmeDNS: + description: |- + Use the 'ACME DNS' (https://github.com/joohoi/acme-dns) API to manage + DNS01 challenge records. + properties: + accountSecretRef: + description: |- + A reference to a specific 'key' within a Secret resource. + In some instances, `key` is a required field. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + host: + type: string + required: + - accountSecretRef + - host + type: object + akamai: + description: Use the Akamai DNS zone management API to manage DNS01 challenge records. + properties: + accessTokenSecretRef: + description: |- + A reference to a specific 'key' within a Secret resource. + In some instances, `key` is a required field. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + clientSecretSecretRef: + description: |- + A reference to a specific 'key' within a Secret resource. + In some instances, `key` is a required field. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + clientTokenSecretRef: + description: |- + A reference to a specific 'key' within a Secret resource. + In some instances, `key` is a required field. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + serviceConsumerDomain: + type: string + required: + - accessTokenSecretRef + - clientSecretSecretRef + - clientTokenSecretRef + - serviceConsumerDomain + type: object + azureDNS: + description: Use the Microsoft Azure DNS API to manage DNS01 challenge records. + properties: + clientID: + description: |- + Auth: Azure Service Principal: + The ClientID of the Azure Service Principal used to authenticate with Azure DNS. + If set, ClientSecret and TenantID must also be set. + type: string + clientSecretSecretRef: + description: |- + Auth: Azure Service Principal: + A reference to a Secret containing the password associated with the Service Principal. + If set, ClientID and TenantID must also be set. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + environment: + description: name of the Azure environment (default AzurePublicCloud) + enum: + - AzurePublicCloud + - AzureChinaCloud + - AzureGermanCloud + - AzureUSGovernmentCloud + type: string + hostedZoneName: + description: name of the DNS zone that should be used + type: string + managedIdentity: + description: |- + Auth: Azure Workload Identity or Azure Managed Service Identity: + Settings to enable Azure Workload Identity or Azure Managed Service Identity + If set, ClientID, ClientSecret and TenantID must not be set. + properties: + clientID: + description: client ID of the managed identity, cannot be used at the same time as resourceID + type: string + resourceID: + description: |- + resource ID of the managed identity, cannot be used at the same time as clientID + Cannot be used for Azure Managed Service Identity + type: string + tenantID: + description: tenant ID of the managed identity, cannot be used at the same time as resourceID + type: string + type: object + resourceGroupName: + description: resource group the DNS zone is located in + type: string + subscriptionID: + description: ID of the Azure subscription + type: string + tenantID: + description: |- + Auth: Azure Service Principal: + The TenantID of the Azure Service Principal used to authenticate with Azure DNS. + If set, ClientID and ClientSecret must also be set. + type: string + required: + - resourceGroupName + - subscriptionID + type: object + cloudDNS: + description: Use the Google Cloud DNS API to manage DNS01 challenge records. + properties: + hostedZoneName: + description: |- + HostedZoneName is an optional field that tells cert-manager in which + Cloud DNS zone the challenge record has to be created. + If left empty cert-manager will automatically choose a zone. + type: string + project: + type: string + serviceAccountSecretRef: + description: |- + A reference to a specific 'key' within a Secret resource. + In some instances, `key` is a required field. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + required: + - project + type: object + cloudflare: + description: Use the Cloudflare API to manage DNS01 challenge records. + properties: + apiKeySecretRef: + description: |- + API key to use to authenticate with Cloudflare. + Note: using an API token to authenticate is now the recommended method + as it allows greater control of permissions. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + apiTokenSecretRef: + description: API token used to authenticate with Cloudflare. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + email: + description: Email of the account, only required when using API key based authentication. + type: string + type: object + cnameStrategy: + description: |- + CNAMEStrategy configures how the DNS01 provider should handle CNAME + records when found in DNS zones. + enum: + - None + - Follow + type: string + digitalocean: + description: Use the DigitalOcean DNS API to manage DNS01 challenge records. + properties: + tokenSecretRef: + description: |- + A reference to a specific 'key' within a Secret resource. + In some instances, `key` is a required field. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + required: + - tokenSecretRef + type: object + rfc2136: + description: |- + Use RFC2136 ("Dynamic Updates in the Domain Name System") (https://datatracker.ietf.org/doc/rfc2136/) + to manage DNS01 challenge records. + properties: + nameserver: + description: |- + The IP address or hostname of an authoritative DNS server supporting + RFC2136 in the form host:port. If the host is an IPv6 address it must be + enclosed in square brackets (e.g [2001:db8::1]) ; port is optional. + This field is required. + type: string + protocol: + description: Protocol to use for dynamic DNS update queries. Valid values are (case-sensitive) ``TCP`` and ``UDP``; ``UDP`` (default). + enum: + - TCP + - UDP + type: string + tsigAlgorithm: + description: |- + The TSIG Algorithm configured in the DNS supporting RFC2136. Used only + when ``tsigSecretSecretRef`` and ``tsigKeyName`` are defined. + Supported values are (case-insensitive): ``HMACMD5`` (default), + ``HMACSHA1``, ``HMACSHA256`` or ``HMACSHA512``. + type: string + tsigKeyName: + description: |- + The TSIG Key name configured in the DNS. + If ``tsigSecretSecretRef`` is defined, this field is required. + type: string + tsigSecretSecretRef: + description: |- + The name of the secret containing the TSIG value. + If ``tsigKeyName`` is defined, this field is required. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + required: + - nameserver + type: object + route53: + description: Use the AWS Route53 API to manage DNS01 challenge records. + properties: + accessKeyID: + description: |- + The AccessKeyID is used for authentication. + Cannot be set when SecretAccessKeyID is set. + If neither the Access Key nor Key ID are set, we fall-back to using env + vars, shared credentials file or AWS Instance metadata, + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials + type: string + accessKeyIDSecretRef: + description: |- + The SecretAccessKey is used for authentication. If set, pull the AWS + access key ID from a key within a Kubernetes Secret. + Cannot be set when AccessKeyID is set. + If neither the Access Key nor Key ID are set, we fall-back to using env + vars, shared credentials file or AWS Instance metadata, + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + auth: + description: Auth configures how cert-manager authenticates. + properties: + kubernetes: + description: |- + Kubernetes authenticates with Route53 using AssumeRoleWithWebIdentity + by passing a bound ServiceAccount token. + properties: + serviceAccountRef: + description: |- + A reference to a service account that will be used to request a bound + token (also known as "projected token"). To use this field, you must + configure an RBAC rule to let cert-manager request a token. + properties: + audiences: + description: |- + TokenAudiences is an optional list of audiences to include in the + token passed to AWS. The default token consisting of the issuer's namespace + and name is always included. + If unset the audience defaults to `sts.amazonaws.com`. + items: + type: string + type: array + x-kubernetes-list-type: atomic + name: + description: Name of the ServiceAccount used to request a token. + type: string + required: + - name + type: object + required: + - serviceAccountRef + type: object + required: + - kubernetes + type: object + hostedZoneID: + description: If set, the provider will manage only this zone in Route53 and will not do a lookup using the route53:ListHostedZonesByName api call. + type: string + region: + description: |- + Override the AWS region. + + Route53 is a global service and does not have regional endpoints but the + region specified here (or via environment variables) is used as a hint to + help compute the correct AWS credential scope and partition when it + connects to Route53. See: + - [Amazon Route 53 endpoints and quotas](https://docs.aws.amazon.com/general/latest/gr/r53.html) + - [Global services](https://docs.aws.amazon.com/whitepapers/latest/aws-fault-isolation-boundaries/global-services.html) + + If you omit this region field, cert-manager will use the region from + AWS_REGION and AWS_DEFAULT_REGION environment variables, if they are set + in the cert-manager controller Pod. + + The `region` field is not needed if you use [IAM Roles for Service Accounts (IRSA)](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html). + Instead an AWS_REGION environment variable is added to the cert-manager controller Pod by: + [Amazon EKS Pod Identity Webhook](https://github.com/aws/amazon-eks-pod-identity-webhook). + In this case this `region` field value is ignored. + + The `region` field is not needed if you use [EKS Pod Identities](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html). + Instead an AWS_REGION environment variable is added to the cert-manager controller Pod by: + [Amazon EKS Pod Identity Agent](https://github.com/aws/eks-pod-identity-agent), + In this case this `region` field value is ignored. + type: string + role: + description: |- + Role is a Role ARN which the Route53 provider will assume using either the explicit credentials AccessKeyID/SecretAccessKey + or the inferred credentials from environment variables, shared credentials file or AWS Instance metadata + type: string + secretAccessKeySecretRef: + description: |- + The SecretAccessKey is used for authentication. + If neither the Access Key nor Key ID are set, we fall-back to using env + vars, shared credentials file or AWS Instance metadata, + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + type: object + webhook: + description: |- + Configure an external webhook based DNS01 challenge solver to manage + DNS01 challenge records. + properties: + config: + description: |- + Additional configuration that should be passed to the webhook apiserver + when challenges are processed. + This can contain arbitrary JSON data. + Secret values should not be specified in this stanza. + If secret values are needed (e.g., credentials for a DNS service), you + should use a SecretKeySelector to reference a Secret resource. + For details on the schema of this field, consult the webhook provider + implementation's documentation. + x-kubernetes-preserve-unknown-fields: true + groupName: + description: |- + The API group name that should be used when POSTing ChallengePayload + resources to the webhook apiserver. + This should be the same as the GroupName specified in the webhook + provider implementation. + type: string + solverName: + description: |- + The name of the solver to use, as defined in the webhook provider + implementation. + This will typically be the name of the provider, e.g., 'cloudflare'. + type: string + required: + - groupName + - solverName + type: object + type: object + http01: + description: |- + Configures cert-manager to attempt to complete authorizations by + performing the HTTP01 challenge flow. + It is not possible to obtain certificates for wildcard domain names + (e.g., `*.example.com`) using the HTTP01 challenge mechanism. + properties: + gatewayHTTPRoute: + description: |- + The Gateway API is a sig-network community API that models service networking + in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will + create HTTPRoutes with the specified labels in the same namespace as the challenge. + This solver is experimental, and fields / behaviour may change in the future. + properties: + labels: + additionalProperties: + type: string + description: |- + Custom labels that will be applied to HTTPRoutes created by cert-manager + while solving HTTP-01 challenges. + type: object + parentRefs: + description: |- + When solving an HTTP-01 challenge, cert-manager creates an HTTPRoute. + cert-manager needs to know which parentRefs should be used when creating + the HTTPRoute. Usually, the parentRef references a Gateway. See: + https://gateway-api.sigs.k8s.io/api-types/httproute/#attaching-to-gateways + items: + description: |- + ParentReference identifies an API object (usually a Gateway) that can be considered + a parent of this resource (usually a route). There are two kinds of parent resources + with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + This API may be extended in the future to support additional kinds of parent + resources. + + The API object must be valid in the cluster; the Group and Kind must + be registered in the cluster for this reference to be valid. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + ParentRefs from a Route to a Service in the same namespace are "producer" + routes, which apply default routing rules to inbound connections from + any namespace to the Service. + + ParentRefs from a Route to a Service in a different namespace are + "consumer" routes, and these routing rules are only applied to outbound + connections originating from the same namespace as the Route, for which + the intended destination of the connections are a Service targeted as a + ParentRef of the Route. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + When the parent resource is a Service, this targets a specific port in the + Service spec. When both Port (experimental) and SectionName are specified, + the name and port of the selected port must match both specified values. + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + SectionName is the name of a section within the target resource. In the + following resources, SectionName is interpreted as the following: + + * Gateway: Listener name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + + Implementations MAY choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName is + interpreted. + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + type: array + x-kubernetes-list-type: atomic + podTemplate: + description: |- + Optional pod template used to configure the ACME challenge solver pods + used for HTTP01 challenges. + properties: + metadata: + description: |- + ObjectMeta overrides for the pod used to solve HTTP01 challenges. + Only the 'labels' and 'annotations' fields may be set. + If labels or annotations overlap with in-built values, the values here + will override the in-built values. + properties: + annotations: + additionalProperties: + type: string + description: Annotations that should be added to the created ACME HTTP01 solver pods. + type: object + labels: + additionalProperties: + type: string + description: Labels that should be added to the created ACME HTTP01 solver pods. + type: object + type: object + spec: + description: |- + PodSpec defines overrides for the HTTP01 challenge solver pod. + Check ACMEChallengeSolverHTTP01IngressPodSpec to find out currently supported fields. + All other fields will be ignored. + properties: + affinity: + description: If specified, the pod's scheduling constraints + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and subtracting + "weight" from the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + imagePullSecrets: + description: If specified, the pod's imagePullSecrets + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector is a selector which must be true for the pod to fit on a node. + Selector which must match a node's labels for the pod to be scheduled on that node. + More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + type: object + priorityClassName: + description: If specified, the pod's priorityClassName. + type: string + resources: + description: |- + If specified, the pod's resource requirements. + These values override the global resource configuration flags. + Note that when only specifying resource limits, ensure they are greater than or equal + to the corresponding global resource requests configured via controller flags + (--acme-http01-solver-resource-request-cpu, --acme-http01-solver-resource-request-memory). + Kubernetes will reject pod creation if limits are lower than requests, causing challenge failures. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to the global values configured via controller flags. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + securityContext: + description: If specified, the pod's security context + properties: + fsGroup: + description: |- + A special supplemental group that applies to all containers in a pod. + Some volume types allow the Kubelet to change the ownership of that volume + to be owned by the pod: + + 1. The owning GID will be the FSGroup + 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) + 3. The permission bits are OR'd with rw-rw---- + + If unset, the Kubelet will not modify the ownership and permissions of any volume. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + fsGroupChangePolicy: + description: |- + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume + before being exposed inside Pod. This field will only apply to + volume types which support fsGroup based ownership(and permissions). + It will have no effect on ephemeral volume types such as: secret, configmaps + and emptydir. + Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. + Note that this field cannot be set when spec.os.name is windows. + type: string + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in SecurityContext. If set in + both SecurityContext and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies to the container. + type: string + role: + description: Role is a SELinux role label that applies to the container. + type: string + type: + description: Type is a SELinux type label that applies to the container. + type: string + user: + description: User is a SELinux user label that applies to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + supplementalGroups: + description: |- + A list of groups applied to the first process run in each container, in addition + to the container's primary GID, the fsGroup (if specified), and group memberships + defined in the container image for the uid of the container process. If unspecified, + no additional groups are added to any container. Note that group memberships + defined in the container image for the uid of the container process are still effective, + even if they are not included in this list. + Note that this field cannot be set when spec.os.name is windows. + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + sysctls: + description: |- + Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported + sysctls (by the container runtime) might fail to launch. + Note that this field cannot be set when spec.os.name is windows. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + type: object + serviceAccountName: + description: If specified, the pod's service account + type: string + tolerations: + description: If specified, the pod's tolerations. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + serviceType: + description: |- + Optional service type for Kubernetes solver service. Supported values + are NodePort or ClusterIP. If unset, defaults to NodePort. + type: string + type: object + ingress: + description: |- + The ingress based HTTP01 challenge solver will solve challenges by + creating or modifying Ingress resources in order to route requests for + '/.well-known/acme-challenge/XYZ' to 'challenge solver' pods that are + provisioned by cert-manager for each Challenge to be completed. + properties: + class: + description: |- + This field configures the annotation `kubernetes.io/ingress.class` when + creating Ingress resources to solve ACME challenges that use this + challenge solver. Only one of `class`, `name` or `ingressClassName` may + be specified. + type: string + ingressClassName: + description: |- + This field configures the field `ingressClassName` on the created Ingress + resources used to solve ACME challenges that use this challenge solver. + This is the recommended way of configuring the ingress class. Only one of + `class`, `name` or `ingressClassName` may be specified. + type: string + ingressTemplate: + description: |- + Optional ingress template used to configure the ACME challenge solver + ingress used for HTTP01 challenges. + properties: + metadata: + description: |- + ObjectMeta overrides for the ingress used to solve HTTP01 challenges. + Only the 'labels' and 'annotations' fields may be set. + If labels or annotations overlap with in-built values, the values here + will override the in-built values. + properties: + annotations: + additionalProperties: + type: string + description: Annotations that should be added to the created ACME HTTP01 solver ingress. + type: object + labels: + additionalProperties: + type: string + description: Labels that should be added to the created ACME HTTP01 solver ingress. + type: object + type: object + type: object + name: + description: |- + The name of the ingress resource that should have ACME challenge solving + routes inserted into it in order to solve HTTP01 challenges. + This is typically used in conjunction with ingress controllers like + ingress-gce, which maintains a 1:1 mapping between external IPs and + ingress resources. Only one of `class`, `name` or `ingressClassName` may + be specified. + type: string + podTemplate: + description: |- + Optional pod template used to configure the ACME challenge solver pods + used for HTTP01 challenges. + properties: + metadata: + description: |- + ObjectMeta overrides for the pod used to solve HTTP01 challenges. + Only the 'labels' and 'annotations' fields may be set. + If labels or annotations overlap with in-built values, the values here + will override the in-built values. + properties: + annotations: + additionalProperties: + type: string + description: Annotations that should be added to the created ACME HTTP01 solver pods. + type: object + labels: + additionalProperties: + type: string + description: Labels that should be added to the created ACME HTTP01 solver pods. + type: object + type: object + spec: + description: |- + PodSpec defines overrides for the HTTP01 challenge solver pod. + Check ACMEChallengeSolverHTTP01IngressPodSpec to find out currently supported fields. + All other fields will be ignored. + properties: + affinity: + description: If specified, the pod's scheduling constraints + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and subtracting + "weight" from the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + imagePullSecrets: + description: If specified, the pod's imagePullSecrets + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector is a selector which must be true for the pod to fit on a node. + Selector which must match a node's labels for the pod to be scheduled on that node. + More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + type: object + priorityClassName: + description: If specified, the pod's priorityClassName. + type: string + resources: + description: |- + If specified, the pod's resource requirements. + These values override the global resource configuration flags. + Note that when only specifying resource limits, ensure they are greater than or equal + to the corresponding global resource requests configured via controller flags + (--acme-http01-solver-resource-request-cpu, --acme-http01-solver-resource-request-memory). + Kubernetes will reject pod creation if limits are lower than requests, causing challenge failures. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to the global values configured via controller flags. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + securityContext: + description: If specified, the pod's security context + properties: + fsGroup: + description: |- + A special supplemental group that applies to all containers in a pod. + Some volume types allow the Kubelet to change the ownership of that volume + to be owned by the pod: + + 1. The owning GID will be the FSGroup + 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) + 3. The permission bits are OR'd with rw-rw---- + + If unset, the Kubelet will not modify the ownership and permissions of any volume. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + fsGroupChangePolicy: + description: |- + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume + before being exposed inside Pod. This field will only apply to + volume types which support fsGroup based ownership(and permissions). + It will have no effect on ephemeral volume types such as: secret, configmaps + and emptydir. + Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. + Note that this field cannot be set when spec.os.name is windows. + type: string + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in SecurityContext. If set in + both SecurityContext and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies to the container. + type: string + role: + description: Role is a SELinux role label that applies to the container. + type: string + type: + description: Type is a SELinux type label that applies to the container. + type: string + user: + description: User is a SELinux user label that applies to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + supplementalGroups: + description: |- + A list of groups applied to the first process run in each container, in addition + to the container's primary GID, the fsGroup (if specified), and group memberships + defined in the container image for the uid of the container process. If unspecified, + no additional groups are added to any container. Note that group memberships + defined in the container image for the uid of the container process are still effective, + even if they are not included in this list. + Note that this field cannot be set when spec.os.name is windows. + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + sysctls: + description: |- + Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported + sysctls (by the container runtime) might fail to launch. + Note that this field cannot be set when spec.os.name is windows. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + type: object + serviceAccountName: + description: If specified, the pod's service account + type: string + tolerations: + description: If specified, the pod's tolerations. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + serviceType: + description: |- + Optional service type for Kubernetes solver service. Supported values + are NodePort or ClusterIP. If unset, defaults to NodePort. + type: string + type: object + type: object + selector: + description: |- + Selector selects a set of DNSNames on the Certificate resource that + should be solved using this challenge solver. + If not specified, the solver will be treated as the 'default' solver + with the lowest priority, i.e. if any other solver has a more specific + match, it will be used instead. + properties: + dnsNames: + description: |- + List of DNSNames that this solver will be used to solve. + If specified and a match is found, a dnsNames selector will take + precedence over a dnsZones selector. + If multiple solvers match with the same dnsNames value, the solver + with the most matching labels in matchLabels will be selected. + If neither has more matches, the solver defined earlier in the list + will be selected. + items: + type: string + type: array + x-kubernetes-list-type: atomic + dnsZones: + description: |- + List of DNSZones that this solver will be used to solve. + The most specific DNS zone match specified here will take precedence + over other DNS zone matches, so a solver specifying sys.example.com + will be selected over one specifying example.com for the domain + www.sys.example.com. + If multiple solvers match with the same dnsZones value, the solver + with the most matching labels in matchLabels will be selected. + If neither has more matches, the solver defined earlier in the list + will be selected. + items: + type: string + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + A label selector that is used to refine the set of certificate's that + this challenge solver will apply to. + type: object + type: object + type: object + token: + description: |- + The ACME challenge token for this challenge. + This is the raw value returned from the ACME server. + type: string + type: + description: |- + The type of ACME challenge this resource represents. + One of "HTTP-01" or "DNS-01". + enum: + - HTTP-01 + - DNS-01 + type: string + url: + description: |- + The URL of the ACME Challenge resource for this challenge. + This can be used to lookup details about the status of this challenge. + type: string + wildcard: + description: |- + wildcard will be true if this challenge is for a wildcard identifier, + for example '*.example.com'. + type: boolean + required: + - authorizationURL + - dnsName + - issuerRef + - key + - solver + - token + - type + - url + type: object + status: + properties: + presented: + description: |- + presented will be set to true if the challenge values for this challenge + are currently 'presented'. + This *does not* imply the self check is passing. Only that the values + have been 'submitted' for the appropriate challenge mechanism (i.e. the + DNS01 TXT record has been presented, or the HTTP01 configuration has been + configured). + type: boolean + processing: + description: |- + Used to denote whether this challenge should be processed or not. + This field will only be set to true by the 'scheduling' component. + It will only be set to false by the 'challenges' controller, after the + challenge has reached a final state or timed out. + If this field is set to false, the challenge controller will not take + any more action. + type: boolean + reason: + description: |- + Contains human readable information on why the Challenge is in the + current state. + type: string + state: + description: |- + Contains the current 'state' of the challenge. + If not set, the state of the challenge is unknown. + enum: + - valid + - ready + - pending + - processing + - invalid + - expired + - errored + type: string + type: object + required: + - metadata + - spec + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/packages/system/cert-manager-crds/templates/crd-acme.cert-manager.io_orders.yaml b/packages/system/cert-manager-crds/templates/crd-acme.cert-manager.io_orders.yaml new file mode 100644 index 00000000..3242fc41 --- /dev/null +++ b/packages/system/cert-manager-crds/templates/crd-acme.cert-manager.io_orders.yaml @@ -0,0 +1,274 @@ +{{- if or .Values.crds.enabled .Values.installCRDs }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: "orders.acme.cert-manager.io" + {{- if .Values.crds.keep }} + annotations: + helm.sh/resource-policy: keep + {{- end }} + labels: + {{- include "cert-manager.crd-labels" . | nindent 4 }} +spec: + group: acme.cert-manager.io + names: + categories: + - cert-manager + - cert-manager-acme + kind: Order + listKind: OrderList + plural: orders + singular: order + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.state + name: State + type: string + - jsonPath: .spec.issuerRef.name + name: Issuer + priority: 1 + type: string + - jsonPath: .status.reason + name: Reason + priority: 1 + type: string + - description: CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC. + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: Order is a type to represent an Order with an ACME server + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + properties: + commonName: + description: |- + CommonName is the common name as specified on the DER encoded CSR. + If specified, this value must also be present in `dnsNames` or `ipAddresses`. + This field must match the corresponding field on the DER encoded CSR. + type: string + dnsNames: + description: |- + DNSNames is a list of DNS names that should be included as part of the Order + validation process. + This field must match the corresponding field on the DER encoded CSR. + items: + type: string + type: array + x-kubernetes-list-type: atomic + duration: + description: |- + Duration is the duration for the not after date for the requested certificate. + this is set on order creation as pe the ACME spec. + type: string + ipAddresses: + description: |- + IPAddresses is a list of IP addresses that should be included as part of the Order + validation process. + This field must match the corresponding field on the DER encoded CSR. + items: + type: string + type: array + x-kubernetes-list-type: atomic + issuerRef: + description: |- + IssuerRef references a properly configured ACME-type Issuer which should + be used to create this Order. + If the Issuer does not exist, processing will be retried. + If the Issuer is not an 'ACME' Issuer, an error will be returned and the + Order will be marked as failed. + properties: + group: + description: |- + Group of the issuer being referred to. + Defaults to 'cert-manager.io'. + type: string + kind: + description: |- + Kind of the issuer being referred to. + Defaults to 'Issuer'. + type: string + name: + description: Name of the issuer being referred to. + type: string + required: + - name + type: object + profile: + description: |- + Profile allows requesting a certificate profile from the ACME server. + Supported profiles are listed by the server's ACME directory URL. + type: string + request: + description: |- + Certificate signing request bytes in DER encoding. + This will be used when finalizing the order. + This field must be set on the order. + format: byte + type: string + required: + - issuerRef + - request + type: object + status: + properties: + authorizations: + description: |- + Authorizations contains data returned from the ACME server on what + authorizations must be completed in order to validate the DNS names + specified on the Order. + items: + description: |- + ACMEAuthorization contains data returned from the ACME server on an + authorization that must be completed in order validate a DNS name on an ACME + Order resource. + properties: + challenges: + description: |- + Challenges specifies the challenge types offered by the ACME server. + One of these challenge types will be selected when validating the DNS + name and an appropriate Challenge resource will be created to perform + the ACME challenge process. + items: + description: |- + Challenge specifies a challenge offered by the ACME server for an Order. + An appropriate Challenge resource can be created to perform the ACME + challenge process. + properties: + token: + description: |- + Token is the token that must be presented for this challenge. + This is used to compute the 'key' that must also be presented. + type: string + type: + description: |- + Type is the type of challenge being offered, e.g., 'http-01', 'dns-01', + 'tls-sni-01', etc. + This is the raw value retrieved from the ACME server. + Only 'http-01' and 'dns-01' are supported by cert-manager, other values + will be ignored. + type: string + url: + description: |- + URL is the URL of this challenge. It can be used to retrieve additional + metadata about the Challenge from the ACME server. + type: string + required: + - token + - type + - url + type: object + type: array + x-kubernetes-list-type: atomic + identifier: + description: Identifier is the DNS name to be validated as part of this authorization + type: string + initialState: + description: |- + InitialState is the initial state of the ACME authorization when first + fetched from the ACME server. + If an Authorization is already 'valid', the Order controller will not + create a Challenge resource for the authorization. This will occur when + working with an ACME server that enables 'authz reuse' (such as Let's + Encrypt's production endpoint). + If not set and 'identifier' is set, the state is assumed to be pending + and a Challenge will be created. + enum: + - valid + - ready + - pending + - processing + - invalid + - expired + - errored + type: string + url: + description: URL is the URL of the Authorization that must be completed + type: string + wildcard: + description: |- + Wildcard will be true if this authorization is for a wildcard DNS name. + If this is true, the identifier will be the *non-wildcard* version of + the DNS name. + For example, if '*.example.com' is the DNS name being validated, this + field will be 'true' and the 'identifier' field will be 'example.com'. + type: boolean + required: + - url + type: object + type: array + x-kubernetes-list-type: atomic + certificate: + description: |- + Certificate is a copy of the PEM encoded certificate for this Order. + This field will be populated after the order has been successfully + finalized with the ACME server, and the order has transitioned to the + 'valid' state. + format: byte + type: string + failureTime: + description: |- + FailureTime stores the time that this order failed. + This is used to influence garbage collection and back-off. + format: date-time + type: string + finalizeURL: + description: |- + FinalizeURL of the Order. + This is used to obtain certificates for this order once it has been completed. + type: string + reason: + description: |- + Reason optionally provides more information about a why the order is in + the current state. + type: string + state: + description: |- + State contains the current state of this Order resource. + States 'success' and 'expired' are 'final' + enum: + - valid + - ready + - pending + - processing + - invalid + - expired + - errored + type: string + url: + description: |- + URL of the Order. + This will initially be empty when the resource is first created. + The Order controller will populate this field when the Order is first processed. + This field will be immutable after it is initially set. + type: string + type: object + required: + - metadata + - spec + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/packages/system/cert-manager-crds/templates/crd-cert-manager.io_certificaterequests.yaml b/packages/system/cert-manager-crds/templates/crd-cert-manager.io_certificaterequests.yaml new file mode 100644 index 00000000..e25ad1d0 --- /dev/null +++ b/packages/system/cert-manager-crds/templates/crd-cert-manager.io_certificaterequests.yaml @@ -0,0 +1,319 @@ +{{- if or .Values.crds.enabled .Values.installCRDs }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: "certificaterequests.cert-manager.io" + {{- if .Values.crds.keep }} + annotations: + helm.sh/resource-policy: keep + {{- end }} + labels: + {{- include "cert-manager.crd-labels" . | nindent 4 }} +spec: + group: cert-manager.io + names: + categories: + - cert-manager + kind: CertificateRequest + listKind: CertificateRequestList + plural: certificaterequests + shortNames: + - cr + - crs + singular: certificaterequest + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type == "Approved")].status + name: Approved + type: string + - jsonPath: .status.conditions[?(@.type == "Denied")].status + name: Denied + type: string + - jsonPath: .status.conditions[?(@.type == "Ready")].status + name: Ready + type: string + - jsonPath: .spec.issuerRef.name + name: Issuer + type: string + - jsonPath: .spec.username + name: Requester + type: string + - jsonPath: .status.conditions[?(@.type == "Ready")].message + name: Status + priority: 1 + type: string + - description: CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC. + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: |- + A CertificateRequest is used to request a signed certificate from one of the + configured issuers. + + All fields within the CertificateRequest's `spec` are immutable after creation. + A CertificateRequest will either succeed or fail, as denoted by its `Ready` status + condition and its `status.failureTime` field. + + A CertificateRequest is a one-shot resource, meaning it represents a single + point in time request for a certificate and cannot be re-used. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + Specification of the desired state of the CertificateRequest resource. + https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + duration: + description: |- + Requested 'duration' (i.e. lifetime) of the Certificate. Note that the + issuer may choose to ignore the requested duration, just like any other + requested attribute. + type: string + extra: + additionalProperties: + items: + type: string + type: array + description: |- + Extra contains extra attributes of the user that created the CertificateRequest. + Populated by the cert-manager webhook on creation and immutable. + type: object + groups: + description: |- + Groups contains group membership of the user that created the CertificateRequest. + Populated by the cert-manager webhook on creation and immutable. + items: + type: string + type: array + x-kubernetes-list-type: atomic + isCA: + description: |- + Requested basic constraints isCA value. Note that the issuer may choose + to ignore the requested isCA value, just like any other requested attribute. + + NOTE: If the CSR in the `Request` field has a BasicConstraints extension, + it must have the same isCA value as specified here. + + If true, this will automatically add the `cert sign` usage to the list + of requested `usages`. + type: boolean + issuerRef: + description: |- + Reference to the issuer responsible for issuing the certificate. + If the issuer is namespace-scoped, it must be in the same namespace + as the Certificate. If the issuer is cluster-scoped, it can be used + from any namespace. + + The `name` field of the reference must always be specified. + properties: + group: + description: |- + Group of the issuer being referred to. + Defaults to 'cert-manager.io'. + type: string + kind: + description: |- + Kind of the issuer being referred to. + Defaults to 'Issuer'. + type: string + name: + description: Name of the issuer being referred to. + type: string + required: + - name + type: object + request: + description: |- + The PEM-encoded X.509 certificate signing request to be submitted to the + issuer for signing. + + If the CSR has a BasicConstraints extension, its isCA attribute must + match the `isCA` value of this CertificateRequest. + If the CSR has a KeyUsage extension, its key usages must match the + key usages in the `usages` field of this CertificateRequest. + If the CSR has a ExtKeyUsage extension, its extended key usages + must match the extended key usages in the `usages` field of this + CertificateRequest. + format: byte + type: string + uid: + description: |- + UID contains the uid of the user that created the CertificateRequest. + Populated by the cert-manager webhook on creation and immutable. + type: string + usages: + description: |- + Requested key usages and extended key usages. + + NOTE: If the CSR in the `Request` field has uses the KeyUsage or + ExtKeyUsage extension, these extensions must have the same values + as specified here without any additional values. + + If unset, defaults to `digital signature` and `key encipherment`. + items: + description: |- + KeyUsage specifies valid usage contexts for keys. + See: + https://tools.ietf.org/html/rfc5280#section-4.2.1.3 + https://tools.ietf.org/html/rfc5280#section-4.2.1.12 + + Valid KeyUsage values are as follows: + "signing", + "digital signature", + "content commitment", + "key encipherment", + "key agreement", + "data encipherment", + "cert sign", + "crl sign", + "encipher only", + "decipher only", + "any", + "server auth", + "client auth", + "code signing", + "email protection", + "s/mime", + "ipsec end system", + "ipsec tunnel", + "ipsec user", + "timestamping", + "ocsp signing", + "microsoft sgc", + "netscape sgc" + enum: + - signing + - digital signature + - content commitment + - key encipherment + - key agreement + - data encipherment + - cert sign + - crl sign + - encipher only + - decipher only + - any + - server auth + - client auth + - code signing + - email protection + - s/mime + - ipsec end system + - ipsec tunnel + - ipsec user + - timestamping + - ocsp signing + - microsoft sgc + - netscape sgc + type: string + type: array + x-kubernetes-list-type: atomic + username: + description: |- + Username contains the name of the user that created the CertificateRequest. + Populated by the cert-manager webhook on creation and immutable. + type: string + required: + - issuerRef + - request + type: object + status: + description: |- + Status of the CertificateRequest. + This is set and managed automatically. + Read-only. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + ca: + description: |- + The PEM encoded X.509 certificate of the signer, also known as the CA + (Certificate Authority). + This is set on a best-effort basis by different issuers. + If not set, the CA is assumed to be unknown/not available. + format: byte + type: string + certificate: + description: |- + The PEM encoded X.509 certificate resulting from the certificate + signing request. + If not set, the CertificateRequest has either not been completed or has + failed. More information on failure can be found by checking the + `conditions` field. + format: byte + type: string + conditions: + description: |- + List of status conditions to indicate the status of a CertificateRequest. + Known condition types are `Ready`, `InvalidRequest`, `Approved` and `Denied`. + items: + description: CertificateRequestCondition contains condition information for a CertificateRequest. + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the timestamp corresponding to the last status + change of this condition. + format: date-time + type: string + message: + description: |- + Message is a human readable description of the details of the last + transition, complementing reason. + type: string + reason: + description: |- + Reason is a brief machine readable explanation for the condition's last + transition. + type: string + status: + description: Status of the condition, one of (`True`, `False`, `Unknown`). + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + Type of the condition, known values are (`Ready`, `InvalidRequest`, + `Approved`, `Denied`). + type: string + required: + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + failureTime: + description: |- + FailureTime stores the time that this CertificateRequest failed. This is + used to influence garbage collection and back-off. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/packages/system/cert-manager-crds/templates/crd-cert-manager.io_certificates.yaml b/packages/system/cert-manager-crds/templates/crd-cert-manager.io_certificates.yaml new file mode 100644 index 00000000..6689de66 --- /dev/null +++ b/packages/system/cert-manager-crds/templates/crd-cert-manager.io_certificates.yaml @@ -0,0 +1,816 @@ +{{- if or .Values.crds.enabled .Values.installCRDs }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: "certificates.cert-manager.io" + {{- if .Values.crds.keep }} + annotations: + helm.sh/resource-policy: keep + {{- end }} + labels: + {{- include "cert-manager.crd-labels" . | nindent 4 }} +spec: + group: cert-manager.io + names: + categories: + - cert-manager + kind: Certificate + listKind: CertificateList + plural: certificates + shortNames: + - cert + - certs + singular: certificate + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type == "Ready")].status + name: Ready + type: string + - jsonPath: .spec.secretName + name: Secret + type: string + - jsonPath: .spec.issuerRef.name + name: Issuer + priority: 1 + type: string + - jsonPath: .status.conditions[?(@.type == "Ready")].message + name: Status + priority: 1 + type: string + - description: CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC. + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: |- + A Certificate resource should be created to ensure an up to date and signed + X.509 certificate is stored in the Kubernetes Secret resource named in `spec.secretName`. + + The stored certificate will be renewed before it expires (as configured by `spec.renewBefore`). + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + Specification of the desired state of the Certificate resource. + https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + additionalOutputFormats: + description: |- + Defines extra output formats of the private key and signed certificate chain + to be written to this Certificate's target Secret. + items: + description: |- + CertificateAdditionalOutputFormat defines an additional output format of a + Certificate resource. These contain supplementary data formats of the signed + certificate chain and paired private key. + properties: + type: + description: |- + Type is the name of the format type that should be written to the + Certificate's target Secret. + enum: + - DER + - CombinedPEM + type: string + required: + - type + type: object + type: array + x-kubernetes-list-type: atomic + commonName: + description: |- + Requested common name X509 certificate subject attribute. + More info: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.6 + NOTE: TLS clients will ignore this value when any subject alternative name is + set (see https://tools.ietf.org/html/rfc6125#section-6.4.4). + + Should have a length of 64 characters or fewer to avoid generating invalid CSRs. + Cannot be set if the `literalSubject` field is set. + type: string + dnsNames: + description: Requested DNS subject alternative names. + items: + type: string + type: array + x-kubernetes-list-type: atomic + duration: + description: |- + Requested 'duration' (i.e. lifetime) of the Certificate. Note that the + issuer may choose to ignore the requested duration, just like any other + requested attribute. + + If unset, this defaults to 90 days. + Minimum accepted duration is 1 hour. + Value must be in units accepted by Go time.ParseDuration https://golang.org/pkg/time/#ParseDuration. + type: string + emailAddresses: + description: Requested email subject alternative names. + items: + type: string + type: array + x-kubernetes-list-type: atomic + encodeUsagesInRequest: + description: |- + Whether the KeyUsage and ExtKeyUsage extensions should be set in the encoded CSR. + + This option defaults to true, and should only be disabled if the target + issuer does not support CSRs with these X509 KeyUsage/ ExtKeyUsage extensions. + type: boolean + ipAddresses: + description: Requested IP address subject alternative names. + items: + type: string + type: array + x-kubernetes-list-type: atomic + isCA: + description: |- + Requested basic constraints isCA value. + The isCA value is used to set the `isCA` field on the created CertificateRequest + resources. Note that the issuer may choose to ignore the requested isCA value, just + like any other requested attribute. + + If true, this will automatically add the `cert sign` usage to the list + of requested `usages`. + type: boolean + issuerRef: + description: |- + Reference to the issuer responsible for issuing the certificate. + If the issuer is namespace-scoped, it must be in the same namespace + as the Certificate. If the issuer is cluster-scoped, it can be used + from any namespace. + + The `name` field of the reference must always be specified. + properties: + group: + description: |- + Group of the issuer being referred to. + Defaults to 'cert-manager.io'. + type: string + kind: + description: |- + Kind of the issuer being referred to. + Defaults to 'Issuer'. + type: string + name: + description: Name of the issuer being referred to. + type: string + required: + - name + type: object + keystores: + description: Additional keystore output formats to be stored in the Certificate's Secret. + properties: + jks: + description: |- + JKS configures options for storing a JKS keystore in the + `spec.secretName` Secret resource. + properties: + alias: + description: |- + Alias specifies the alias of the key in the keystore, required by the JKS format. + If not provided, the default alias `certificate` will be used. + type: string + create: + description: |- + Create enables JKS keystore creation for the Certificate. + If true, a file named `keystore.jks` will be created in the target + Secret resource, encrypted using the password stored in + `passwordSecretRef` or `password`. + The keystore file will be updated immediately. + If the issuer provided a CA certificate, a file named `truststore.jks` + will also be created in the target Secret resource, encrypted using the + password stored in `passwordSecretRef` + containing the issuing Certificate Authority + type: boolean + password: + description: |- + Password provides a literal password used to encrypt the JKS keystore. + Mutually exclusive with passwordSecretRef. + One of password or passwordSecretRef must provide a password with a non-zero length. + type: string + passwordSecretRef: + description: |- + PasswordSecretRef is a reference to a non-empty key in a Secret resource + containing the password used to encrypt the JKS keystore. + Mutually exclusive with password. + One of password or passwordSecretRef must provide a password with a non-zero length. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + required: + - create + type: object + pkcs12: + description: |- + PKCS12 configures options for storing a PKCS12 keystore in the + `spec.secretName` Secret resource. + properties: + create: + description: |- + Create enables PKCS12 keystore creation for the Certificate. + If true, a file named `keystore.p12` will be created in the target + Secret resource, encrypted using the password stored in + `passwordSecretRef` or in `password`. + The keystore file will be updated immediately. + If the issuer provided a CA certificate, a file named `truststore.p12` will + also be created in the target Secret resource, encrypted using the + password stored in `passwordSecretRef` containing the issuing Certificate + Authority + type: boolean + password: + description: |- + Password provides a literal password used to encrypt the PKCS#12 keystore. + Mutually exclusive with passwordSecretRef. + One of password or passwordSecretRef must provide a password with a non-zero length. + type: string + passwordSecretRef: + description: |- + PasswordSecretRef is a reference to a non-empty key in a Secret resource + containing the password used to encrypt the PKCS#12 keystore. + Mutually exclusive with password. + One of password or passwordSecretRef must provide a password with a non-zero length. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + profile: + description: |- + Profile specifies the key and certificate encryption algorithms and the HMAC algorithm + used to create the PKCS12 keystore. Default value is `LegacyRC2` for backward compatibility. + + If provided, allowed values are: + `LegacyRC2`: Deprecated. Not supported by default in OpenSSL 3 or Java 20. + `LegacyDES`: Less secure algorithm. Use this option for maximal compatibility. + `Modern2023`: Secure algorithm. Use this option in case you have to always use secure algorithms + (e.g., because of company policy). Please note that the security of the algorithm is not that important + in reality, because the unencrypted certificate and private key are also stored in the Secret. + enum: + - LegacyRC2 + - LegacyDES + - Modern2023 + type: string + required: + - create + type: object + type: object + literalSubject: + description: |- + Requested X.509 certificate subject, represented using the LDAP "String + Representation of a Distinguished Name" [1]. + Important: the LDAP string format also specifies the order of the attributes + in the subject, this is important when issuing certs for LDAP authentication. + Example: `CN=foo,DC=corp,DC=example,DC=com` + More info [1]: https://datatracker.ietf.org/doc/html/rfc4514 + More info: https://github.com/cert-manager/cert-manager/issues/3203 + More info: https://github.com/cert-manager/cert-manager/issues/4424 + + Cannot be set if the `subject` or `commonName` field is set. + type: string + nameConstraints: + description: |- + x.509 certificate NameConstraint extension which MUST NOT be used in a non-CA certificate. + More Info: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.10 + + This is an Alpha Feature and is only enabled with the + `--feature-gates=NameConstraints=true` option set on both + the controller and webhook components. + properties: + critical: + description: if true then the name constraints are marked critical. + type: boolean + excluded: + description: |- + Excluded contains the constraints which must be disallowed. Any name matching a + restriction in the excluded field is invalid regardless + of information appearing in the permitted + properties: + dnsDomains: + description: DNSDomains is a list of DNS domains that are permitted or excluded. + items: + type: string + type: array + x-kubernetes-list-type: atomic + emailAddresses: + description: EmailAddresses is a list of Email Addresses that are permitted or excluded. + items: + type: string + type: array + x-kubernetes-list-type: atomic + ipRanges: + description: |- + IPRanges is a list of IP Ranges that are permitted or excluded. + This should be a valid CIDR notation. + items: + type: string + type: array + x-kubernetes-list-type: atomic + uriDomains: + description: URIDomains is a list of URI domains that are permitted or excluded. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + permitted: + description: Permitted contains the constraints in which the names must be located. + properties: + dnsDomains: + description: DNSDomains is a list of DNS domains that are permitted or excluded. + items: + type: string + type: array + x-kubernetes-list-type: atomic + emailAddresses: + description: EmailAddresses is a list of Email Addresses that are permitted or excluded. + items: + type: string + type: array + x-kubernetes-list-type: atomic + ipRanges: + description: |- + IPRanges is a list of IP Ranges that are permitted or excluded. + This should be a valid CIDR notation. + items: + type: string + type: array + x-kubernetes-list-type: atomic + uriDomains: + description: URIDomains is a list of URI domains that are permitted or excluded. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + type: object + otherNames: + description: |- + `otherNames` is an escape hatch for SAN that allows any type. We currently restrict the support to string like otherNames, cf RFC 5280 p 37 + Any UTF8 String valued otherName can be passed with by setting the keys oid: x.x.x.x and UTF8Value: somevalue for `otherName`. + Most commonly this would be UPN set with oid: 1.3.6.1.4.1.311.20.2.3 + You should ensure that any OID passed is valid for the UTF8String type as we do not explicitly validate this. + items: + properties: + oid: + description: |- + OID is the object identifier for the otherName SAN. + The object identifier must be expressed as a dotted string, for + example, "1.2.840.113556.1.4.221". + type: string + utf8Value: + description: |- + utf8Value is the string value of the otherName SAN. + The utf8Value accepts any valid UTF8 string to set as value for the otherName SAN. + type: string + type: object + type: array + x-kubernetes-list-type: atomic + privateKey: + description: |- + Private key options. These include the key algorithm and size, the used + encoding and the rotation policy. + properties: + algorithm: + description: |- + Algorithm is the private key algorithm of the corresponding private key + for this certificate. + + If provided, allowed values are either `RSA`, `ECDSA` or `Ed25519`. + If `algorithm` is specified and `size` is not provided, + key size of 2048 will be used for `RSA` key algorithm and + key size of 256 will be used for `ECDSA` key algorithm. + key size is ignored when using the `Ed25519` key algorithm. + enum: + - RSA + - ECDSA + - Ed25519 + type: string + encoding: + description: |- + The private key cryptography standards (PKCS) encoding for this + certificate's private key to be encoded in. + + If provided, allowed values are `PKCS1` and `PKCS8` standing for PKCS#1 + and PKCS#8, respectively. + Defaults to `PKCS1` if not specified. + enum: + - PKCS1 + - PKCS8 + type: string + rotationPolicy: + description: |- + RotationPolicy controls how private keys should be regenerated when a + re-issuance is being processed. + + If set to `Never`, a private key will only be generated if one does not + already exist in the target `spec.secretName`. If one does exist but it + does not have the correct algorithm or size, a warning will be raised + to await user intervention. + If set to `Always`, a private key matching the specified requirements + will be generated whenever a re-issuance occurs. + Default is `Always`. + The default was changed from `Never` to `Always` in cert-manager >=v1.18.0. + The new default can be disabled by setting the + `--feature-gates=DefaultPrivateKeyRotationPolicyAlways=false` option on + the controller component. + enum: + - Never + - Always + type: string + size: + description: |- + Size is the key bit size of the corresponding private key for this certificate. + + If `algorithm` is set to `RSA`, valid values are `2048`, `4096` or `8192`, + and will default to `2048` if not specified. + If `algorithm` is set to `ECDSA`, valid values are `256`, `384` or `521`, + and will default to `256` if not specified. + If `algorithm` is set to `Ed25519`, Size is ignored. + No other values are allowed. + type: integer + type: object + renewBefore: + description: |- + How long before the currently issued certificate's expiry cert-manager should + renew the certificate. For example, if a certificate is valid for 60 minutes, + and `renewBefore=10m`, cert-manager will begin to attempt to renew the certificate + 50 minutes after it was issued (i.e. when there are 10 minutes remaining until + the certificate is no longer valid). + + NOTE: The actual lifetime of the issued certificate is used to determine the + renewal time. If an issuer returns a certificate with a different lifetime than + the one requested, cert-manager will use the lifetime of the issued certificate. + + If unset, this defaults to 1/3 of the issued certificate's lifetime. + Minimum accepted value is 5 minutes. + Value must be in units accepted by Go time.ParseDuration https://golang.org/pkg/time/#ParseDuration. + Cannot be set if the `renewBeforePercentage` field is set. + type: string + renewBeforePercentage: + description: |- + `renewBeforePercentage` is like `renewBefore`, except it is a relative percentage + rather than an absolute duration. For example, if a certificate is valid for 60 + minutes, and `renewBeforePercentage=25`, cert-manager will begin to attempt to + renew the certificate 45 minutes after it was issued (i.e. when there are 15 + minutes (25%) remaining until the certificate is no longer valid). + + NOTE: The actual lifetime of the issued certificate is used to determine the + renewal time. If an issuer returns a certificate with a different lifetime than + the one requested, cert-manager will use the lifetime of the issued certificate. + + Value must be an integer in the range (0,100). The minimum effective + `renewBefore` derived from the `renewBeforePercentage` and `duration` fields is 5 + minutes. + Cannot be set if the `renewBefore` field is set. + format: int32 + type: integer + revisionHistoryLimit: + description: |- + The maximum number of CertificateRequest revisions that are maintained in + the Certificate's history. Each revision represents a single `CertificateRequest` + created by this Certificate, either when it was created, renewed, or Spec + was changed. Revisions will be removed by oldest first if the number of + revisions exceeds this number. + + If set, revisionHistoryLimit must be a value of `1` or greater. + Default value is `1`. + format: int32 + type: integer + secretName: + description: |- + Name of the Secret resource that will be automatically created and + managed by this Certificate resource. It will be populated with a + private key and certificate, signed by the denoted issuer. The Secret + resource lives in the same namespace as the Certificate resource. + type: string + secretTemplate: + description: |- + Defines annotations and labels to be copied to the Certificate's Secret. + Labels and annotations on the Secret will be changed as they appear on the + SecretTemplate when added or removed. SecretTemplate annotations are added + in conjunction with, and cannot overwrite, the base set of annotations + cert-manager sets on the Certificate's Secret. + properties: + annotations: + additionalProperties: + type: string + description: Annotations is a key value map to be copied to the target Kubernetes Secret. + type: object + labels: + additionalProperties: + type: string + description: Labels is a key value map to be copied to the target Kubernetes Secret. + type: object + type: object + signatureAlgorithm: + description: |- + Signature algorithm to use. + Allowed values for RSA keys: SHA256WithRSA, SHA384WithRSA, SHA512WithRSA. + Allowed values for ECDSA keys: ECDSAWithSHA256, ECDSAWithSHA384, ECDSAWithSHA512. + Allowed values for Ed25519 keys: PureEd25519. + enum: + - SHA256WithRSA + - SHA384WithRSA + - SHA512WithRSA + - ECDSAWithSHA256 + - ECDSAWithSHA384 + - ECDSAWithSHA512 + - PureEd25519 + type: string + subject: + description: |- + Requested set of X509 certificate subject attributes. + More info: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.6 + + The common name attribute is specified separately in the `commonName` field. + Cannot be set if the `literalSubject` field is set. + properties: + countries: + description: Countries to be used on the Certificate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + localities: + description: Cities to be used on the Certificate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + organizationalUnits: + description: Organizational Units to be used on the Certificate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + organizations: + description: Organizations to be used on the Certificate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + postalCodes: + description: Postal codes to be used on the Certificate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + provinces: + description: State/Provinces to be used on the Certificate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + serialNumber: + description: Serial number to be used on the Certificate. + type: string + streetAddresses: + description: Street addresses to be used on the Certificate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + uris: + description: Requested URI subject alternative names. + items: + type: string + type: array + x-kubernetes-list-type: atomic + usages: + description: |- + Requested key usages and extended key usages. + These usages are used to set the `usages` field on the created CertificateRequest + resources. If `encodeUsagesInRequest` is unset or set to `true`, the usages + will additionally be encoded in the `request` field which contains the CSR blob. + + If unset, defaults to `digital signature` and `key encipherment`. + items: + description: |- + KeyUsage specifies valid usage contexts for keys. + See: + https://tools.ietf.org/html/rfc5280#section-4.2.1.3 + https://tools.ietf.org/html/rfc5280#section-4.2.1.12 + + Valid KeyUsage values are as follows: + "signing", + "digital signature", + "content commitment", + "key encipherment", + "key agreement", + "data encipherment", + "cert sign", + "crl sign", + "encipher only", + "decipher only", + "any", + "server auth", + "client auth", + "code signing", + "email protection", + "s/mime", + "ipsec end system", + "ipsec tunnel", + "ipsec user", + "timestamping", + "ocsp signing", + "microsoft sgc", + "netscape sgc" + enum: + - signing + - digital signature + - content commitment + - key encipherment + - key agreement + - data encipherment + - cert sign + - crl sign + - encipher only + - decipher only + - any + - server auth + - client auth + - code signing + - email protection + - s/mime + - ipsec end system + - ipsec tunnel + - ipsec user + - timestamping + - ocsp signing + - microsoft sgc + - netscape sgc + type: string + type: array + x-kubernetes-list-type: atomic + required: + - issuerRef + - secretName + type: object + status: + description: |- + Status of the Certificate. + This is set and managed automatically. + Read-only. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + conditions: + description: |- + List of status conditions to indicate the status of certificates. + Known condition types are `Ready` and `Issuing`. + items: + description: CertificateCondition contains condition information for a Certificate. + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the timestamp corresponding to the last status + change of this condition. + format: date-time + type: string + message: + description: |- + Message is a human readable description of the details of the last + transition, complementing reason. + type: string + observedGeneration: + description: |- + If set, this represents the .metadata.generation that the condition was + set based upon. + For instance, if .metadata.generation is currently 12, but the + .status.condition[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the Certificate. + format: int64 + type: integer + reason: + description: |- + Reason is a brief machine readable explanation for the condition's last + transition. + type: string + status: + description: Status of the condition, one of (`True`, `False`, `Unknown`). + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: Type of the condition, known values are (`Ready`, `Issuing`). + type: string + required: + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + failedIssuanceAttempts: + description: |- + The number of continuous failed issuance attempts up till now. This + field gets removed (if set) on a successful issuance and gets set to + 1 if unset and an issuance has failed. If an issuance has failed, the + delay till the next issuance will be calculated using formula + time.Hour * 2 ^ (failedIssuanceAttempts - 1). + type: integer + lastFailureTime: + description: |- + LastFailureTime is set only if the latest issuance for this + Certificate failed and contains the time of the failure. If an + issuance has failed, the delay till the next issuance will be + calculated using formula time.Hour * 2 ^ (failedIssuanceAttempts - + 1). If the latest issuance has succeeded this field will be unset. + format: date-time + type: string + nextPrivateKeySecretName: + description: |- + The name of the Secret resource containing the private key to be used + for the next certificate iteration. + The keymanager controller will automatically set this field if the + `Issuing` condition is set to `True`. + It will automatically unset this field when the Issuing condition is + not set or False. + type: string + notAfter: + description: |- + The expiration time of the certificate stored in the secret named + by this resource in `spec.secretName`. + format: date-time + type: string + notBefore: + description: |- + The time after which the certificate stored in the secret named + by this resource in `spec.secretName` is valid. + format: date-time + type: string + renewalTime: + description: |- + RenewalTime is the time at which the certificate will be next + renewed. + If not set, no upcoming renewal is scheduled. + format: date-time + type: string + revision: + description: |- + The current 'revision' of the certificate as issued. + + When a CertificateRequest resource is created, it will have the + `cert-manager.io/certificate-revision` set to one greater than the + current value of this field. + + Upon issuance, this field will be set to the value of the annotation + on the CertificateRequest resource used to issue the certificate. + + Persisting the value on the CertificateRequest resource allows the + certificates controller to know whether a request is part of an old + issuance or if it is part of the ongoing revision's issuance by + checking if the revision value in the annotation is greater than this + field. + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/packages/system/cert-manager-crds/templates/crd-cert-manager.io_clusterissuers.yaml b/packages/system/cert-manager-crds/templates/crd-cert-manager.io_clusterissuers.yaml new file mode 100644 index 00000000..790d4b5c --- /dev/null +++ b/packages/system/cert-manager-crds/templates/crd-cert-manager.io_clusterissuers.yaml @@ -0,0 +1,3815 @@ +{{- if or .Values.crds.enabled .Values.installCRDs }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: "clusterissuers.cert-manager.io" + {{- if .Values.crds.keep }} + annotations: + helm.sh/resource-policy: keep + {{- end }} + labels: + {{- include "cert-manager.crd-labels" . | nindent 4 }} +spec: + group: cert-manager.io + names: + categories: + - cert-manager + kind: ClusterIssuer + listKind: ClusterIssuerList + plural: clusterissuers + shortNames: + - ciss + singular: clusterissuer + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type == "Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type == "Ready")].message + name: Status + priority: 1 + type: string + - description: CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC. + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: |- + A ClusterIssuer represents a certificate issuing authority which can be + referenced as part of `issuerRef` fields. + It is similar to an Issuer, however it is cluster-scoped and therefore can + be referenced by resources that exist in *any* namespace, not just the same + namespace as the referent. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Desired state of the ClusterIssuer resource. + properties: + acme: + description: |- + ACME configures this issuer to communicate with a RFC8555 (ACME) server + to obtain signed x509 certificates. + properties: + caBundle: + description: |- + Base64-encoded bundle of PEM CAs which can be used to validate the certificate + chain presented by the ACME server. + Mutually exclusive with SkipTLSVerify; prefer using CABundle to prevent various + kinds of security vulnerabilities. + If CABundle and SkipTLSVerify are unset, the system certificate bundle inside + the container is used to validate the TLS connection. + format: byte + type: string + disableAccountKeyGeneration: + description: |- + Enables or disables generating a new ACME account key. + If true, the Issuer resource will *not* request a new account but will expect + the account key to be supplied via an existing secret. + If false, the cert-manager system will generate a new ACME account key + for the Issuer. + Defaults to false. + type: boolean + email: + description: |- + Email is the email address to be associated with the ACME account. + This field is optional, but it is strongly recommended to be set. + It will be used to contact you in case of issues with your account or + certificates, including expiry notification emails. + This field may be updated after the account is initially registered. + type: string + enableDurationFeature: + description: |- + Enables requesting a Not After date on certificates that matches the + duration of the certificate. This is not supported by all ACME servers + like Let's Encrypt. If set to true when the ACME server does not support + it, it will create an error on the Order. + Defaults to false. + type: boolean + externalAccountBinding: + description: |- + ExternalAccountBinding is a reference to a CA external account of the ACME + server. + If set, upon registration cert-manager will attempt to associate the given + external account credentials with the registered ACME account. + properties: + keyAlgorithm: + description: |- + Deprecated: keyAlgorithm field exists for historical compatibility + reasons and should not be used. The algorithm is now hardcoded to HS256 + in golang/x/crypto/acme. + enum: + - HS256 + - HS384 + - HS512 + type: string + keyID: + description: keyID is the ID of the CA key that the External Account is bound to. + type: string + keySecretRef: + description: |- + keySecretRef is a Secret Key Selector referencing a data item in a Kubernetes + Secret which holds the symmetric MAC key of the External Account Binding. + The `key` is the index string that is paired with the key data in the + Secret and should not be confused with the key data itself, or indeed with + the External Account Binding keyID above. + The secret key stored in the Secret **must** be un-padded, base64 URL + encoded data. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + required: + - keyID + - keySecretRef + type: object + preferredChain: + description: |- + PreferredChain is the chain to use if the ACME server outputs multiple. + PreferredChain is no guarantee that this one gets delivered by the ACME + endpoint. + For example, for Let's Encrypt's DST cross-sign you would use: + "DST Root CA X3" or "ISRG Root X1" for the newer Let's Encrypt root CA. + This value picks the first certificate bundle in the combined set of + ACME default and alternative chains that has a root-most certificate with + this value as its issuer's commonname. + maxLength: 64 + type: string + privateKeySecretRef: + description: |- + PrivateKey is the name of a Kubernetes Secret resource that will be used to + store the automatically generated ACME account private key. + Optionally, a `key` may be specified to select a specific entry within + the named Secret resource. + If `key` is not specified, a default of `tls.key` will be used. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + profile: + description: |- + Profile allows requesting a certificate profile from the ACME server. + Supported profiles are listed by the server's ACME directory URL. + type: string + server: + description: |- + Server is the URL used to access the ACME server's 'directory' endpoint. + For example, for Let's Encrypt's staging endpoint, you would use: + "https://acme-staging-v02.api.letsencrypt.org/directory". + Only ACME v2 endpoints (i.e. RFC 8555) are supported. + type: string + skipTLSVerify: + description: |- + INSECURE: Enables or disables validation of the ACME server TLS certificate. + If true, requests to the ACME server will not have the TLS certificate chain + validated. + Mutually exclusive with CABundle; prefer using CABundle to prevent various + kinds of security vulnerabilities. + Only enable this option in development environments. + If CABundle and SkipTLSVerify are unset, the system certificate bundle inside + the container is used to validate the TLS connection. + Defaults to false. + type: boolean + solvers: + description: |- + Solvers is a list of challenge solvers that will be used to solve + ACME challenges for the matching domains. + Solver configurations must be provided in order to obtain certificates + from an ACME server. + For more information, see: https://cert-manager.io/docs/configuration/acme/ + items: + description: |- + An ACMEChallengeSolver describes how to solve ACME challenges for the issuer it is part of. + A selector may be provided to use different solving strategies for different DNS names. + Only one of HTTP01 or DNS01 must be provided. + properties: + dns01: + description: |- + Configures cert-manager to attempt to complete authorizations by + performing the DNS01 challenge flow. + properties: + acmeDNS: + description: |- + Use the 'ACME DNS' (https://github.com/joohoi/acme-dns) API to manage + DNS01 challenge records. + properties: + accountSecretRef: + description: |- + A reference to a specific 'key' within a Secret resource. + In some instances, `key` is a required field. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + host: + type: string + required: + - accountSecretRef + - host + type: object + akamai: + description: Use the Akamai DNS zone management API to manage DNS01 challenge records. + properties: + accessTokenSecretRef: + description: |- + A reference to a specific 'key' within a Secret resource. + In some instances, `key` is a required field. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + clientSecretSecretRef: + description: |- + A reference to a specific 'key' within a Secret resource. + In some instances, `key` is a required field. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + clientTokenSecretRef: + description: |- + A reference to a specific 'key' within a Secret resource. + In some instances, `key` is a required field. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + serviceConsumerDomain: + type: string + required: + - accessTokenSecretRef + - clientSecretSecretRef + - clientTokenSecretRef + - serviceConsumerDomain + type: object + azureDNS: + description: Use the Microsoft Azure DNS API to manage DNS01 challenge records. + properties: + clientID: + description: |- + Auth: Azure Service Principal: + The ClientID of the Azure Service Principal used to authenticate with Azure DNS. + If set, ClientSecret and TenantID must also be set. + type: string + clientSecretSecretRef: + description: |- + Auth: Azure Service Principal: + A reference to a Secret containing the password associated with the Service Principal. + If set, ClientID and TenantID must also be set. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + environment: + description: name of the Azure environment (default AzurePublicCloud) + enum: + - AzurePublicCloud + - AzureChinaCloud + - AzureGermanCloud + - AzureUSGovernmentCloud + type: string + hostedZoneName: + description: name of the DNS zone that should be used + type: string + managedIdentity: + description: |- + Auth: Azure Workload Identity or Azure Managed Service Identity: + Settings to enable Azure Workload Identity or Azure Managed Service Identity + If set, ClientID, ClientSecret and TenantID must not be set. + properties: + clientID: + description: client ID of the managed identity, cannot be used at the same time as resourceID + type: string + resourceID: + description: |- + resource ID of the managed identity, cannot be used at the same time as clientID + Cannot be used for Azure Managed Service Identity + type: string + tenantID: + description: tenant ID of the managed identity, cannot be used at the same time as resourceID + type: string + type: object + resourceGroupName: + description: resource group the DNS zone is located in + type: string + subscriptionID: + description: ID of the Azure subscription + type: string + tenantID: + description: |- + Auth: Azure Service Principal: + The TenantID of the Azure Service Principal used to authenticate with Azure DNS. + If set, ClientID and ClientSecret must also be set. + type: string + required: + - resourceGroupName + - subscriptionID + type: object + cloudDNS: + description: Use the Google Cloud DNS API to manage DNS01 challenge records. + properties: + hostedZoneName: + description: |- + HostedZoneName is an optional field that tells cert-manager in which + Cloud DNS zone the challenge record has to be created. + If left empty cert-manager will automatically choose a zone. + type: string + project: + type: string + serviceAccountSecretRef: + description: |- + A reference to a specific 'key' within a Secret resource. + In some instances, `key` is a required field. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + required: + - project + type: object + cloudflare: + description: Use the Cloudflare API to manage DNS01 challenge records. + properties: + apiKeySecretRef: + description: |- + API key to use to authenticate with Cloudflare. + Note: using an API token to authenticate is now the recommended method + as it allows greater control of permissions. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + apiTokenSecretRef: + description: API token used to authenticate with Cloudflare. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + email: + description: Email of the account, only required when using API key based authentication. + type: string + type: object + cnameStrategy: + description: |- + CNAMEStrategy configures how the DNS01 provider should handle CNAME + records when found in DNS zones. + enum: + - None + - Follow + type: string + digitalocean: + description: Use the DigitalOcean DNS API to manage DNS01 challenge records. + properties: + tokenSecretRef: + description: |- + A reference to a specific 'key' within a Secret resource. + In some instances, `key` is a required field. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + required: + - tokenSecretRef + type: object + rfc2136: + description: |- + Use RFC2136 ("Dynamic Updates in the Domain Name System") (https://datatracker.ietf.org/doc/rfc2136/) + to manage DNS01 challenge records. + properties: + nameserver: + description: |- + The IP address or hostname of an authoritative DNS server supporting + RFC2136 in the form host:port. If the host is an IPv6 address it must be + enclosed in square brackets (e.g [2001:db8::1]) ; port is optional. + This field is required. + type: string + protocol: + description: Protocol to use for dynamic DNS update queries. Valid values are (case-sensitive) ``TCP`` and ``UDP``; ``UDP`` (default). + enum: + - TCP + - UDP + type: string + tsigAlgorithm: + description: |- + The TSIG Algorithm configured in the DNS supporting RFC2136. Used only + when ``tsigSecretSecretRef`` and ``tsigKeyName`` are defined. + Supported values are (case-insensitive): ``HMACMD5`` (default), + ``HMACSHA1``, ``HMACSHA256`` or ``HMACSHA512``. + type: string + tsigKeyName: + description: |- + The TSIG Key name configured in the DNS. + If ``tsigSecretSecretRef`` is defined, this field is required. + type: string + tsigSecretSecretRef: + description: |- + The name of the secret containing the TSIG value. + If ``tsigKeyName`` is defined, this field is required. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + required: + - nameserver + type: object + route53: + description: Use the AWS Route53 API to manage DNS01 challenge records. + properties: + accessKeyID: + description: |- + The AccessKeyID is used for authentication. + Cannot be set when SecretAccessKeyID is set. + If neither the Access Key nor Key ID are set, we fall-back to using env + vars, shared credentials file or AWS Instance metadata, + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials + type: string + accessKeyIDSecretRef: + description: |- + The SecretAccessKey is used for authentication. If set, pull the AWS + access key ID from a key within a Kubernetes Secret. + Cannot be set when AccessKeyID is set. + If neither the Access Key nor Key ID are set, we fall-back to using env + vars, shared credentials file or AWS Instance metadata, + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + auth: + description: Auth configures how cert-manager authenticates. + properties: + kubernetes: + description: |- + Kubernetes authenticates with Route53 using AssumeRoleWithWebIdentity + by passing a bound ServiceAccount token. + properties: + serviceAccountRef: + description: |- + A reference to a service account that will be used to request a bound + token (also known as "projected token"). To use this field, you must + configure an RBAC rule to let cert-manager request a token. + properties: + audiences: + description: |- + TokenAudiences is an optional list of audiences to include in the + token passed to AWS. The default token consisting of the issuer's namespace + and name is always included. + If unset the audience defaults to `sts.amazonaws.com`. + items: + type: string + type: array + x-kubernetes-list-type: atomic + name: + description: Name of the ServiceAccount used to request a token. + type: string + required: + - name + type: object + required: + - serviceAccountRef + type: object + required: + - kubernetes + type: object + hostedZoneID: + description: If set, the provider will manage only this zone in Route53 and will not do a lookup using the route53:ListHostedZonesByName api call. + type: string + region: + description: |- + Override the AWS region. + + Route53 is a global service and does not have regional endpoints but the + region specified here (or via environment variables) is used as a hint to + help compute the correct AWS credential scope and partition when it + connects to Route53. See: + - [Amazon Route 53 endpoints and quotas](https://docs.aws.amazon.com/general/latest/gr/r53.html) + - [Global services](https://docs.aws.amazon.com/whitepapers/latest/aws-fault-isolation-boundaries/global-services.html) + + If you omit this region field, cert-manager will use the region from + AWS_REGION and AWS_DEFAULT_REGION environment variables, if they are set + in the cert-manager controller Pod. + + The `region` field is not needed if you use [IAM Roles for Service Accounts (IRSA)](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html). + Instead an AWS_REGION environment variable is added to the cert-manager controller Pod by: + [Amazon EKS Pod Identity Webhook](https://github.com/aws/amazon-eks-pod-identity-webhook). + In this case this `region` field value is ignored. + + The `region` field is not needed if you use [EKS Pod Identities](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html). + Instead an AWS_REGION environment variable is added to the cert-manager controller Pod by: + [Amazon EKS Pod Identity Agent](https://github.com/aws/eks-pod-identity-agent), + In this case this `region` field value is ignored. + type: string + role: + description: |- + Role is a Role ARN which the Route53 provider will assume using either the explicit credentials AccessKeyID/SecretAccessKey + or the inferred credentials from environment variables, shared credentials file or AWS Instance metadata + type: string + secretAccessKeySecretRef: + description: |- + The SecretAccessKey is used for authentication. + If neither the Access Key nor Key ID are set, we fall-back to using env + vars, shared credentials file or AWS Instance metadata, + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + type: object + webhook: + description: |- + Configure an external webhook based DNS01 challenge solver to manage + DNS01 challenge records. + properties: + config: + description: |- + Additional configuration that should be passed to the webhook apiserver + when challenges are processed. + This can contain arbitrary JSON data. + Secret values should not be specified in this stanza. + If secret values are needed (e.g., credentials for a DNS service), you + should use a SecretKeySelector to reference a Secret resource. + For details on the schema of this field, consult the webhook provider + implementation's documentation. + x-kubernetes-preserve-unknown-fields: true + groupName: + description: |- + The API group name that should be used when POSTing ChallengePayload + resources to the webhook apiserver. + This should be the same as the GroupName specified in the webhook + provider implementation. + type: string + solverName: + description: |- + The name of the solver to use, as defined in the webhook provider + implementation. + This will typically be the name of the provider, e.g., 'cloudflare'. + type: string + required: + - groupName + - solverName + type: object + type: object + http01: + description: |- + Configures cert-manager to attempt to complete authorizations by + performing the HTTP01 challenge flow. + It is not possible to obtain certificates for wildcard domain names + (e.g., `*.example.com`) using the HTTP01 challenge mechanism. + properties: + gatewayHTTPRoute: + description: |- + The Gateway API is a sig-network community API that models service networking + in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will + create HTTPRoutes with the specified labels in the same namespace as the challenge. + This solver is experimental, and fields / behaviour may change in the future. + properties: + labels: + additionalProperties: + type: string + description: |- + Custom labels that will be applied to HTTPRoutes created by cert-manager + while solving HTTP-01 challenges. + type: object + parentRefs: + description: |- + When solving an HTTP-01 challenge, cert-manager creates an HTTPRoute. + cert-manager needs to know which parentRefs should be used when creating + the HTTPRoute. Usually, the parentRef references a Gateway. See: + https://gateway-api.sigs.k8s.io/api-types/httproute/#attaching-to-gateways + items: + description: |- + ParentReference identifies an API object (usually a Gateway) that can be considered + a parent of this resource (usually a route). There are two kinds of parent resources + with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + This API may be extended in the future to support additional kinds of parent + resources. + + The API object must be valid in the cluster; the Group and Kind must + be registered in the cluster for this reference to be valid. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + ParentRefs from a Route to a Service in the same namespace are "producer" + routes, which apply default routing rules to inbound connections from + any namespace to the Service. + + ParentRefs from a Route to a Service in a different namespace are + "consumer" routes, and these routing rules are only applied to outbound + connections originating from the same namespace as the Route, for which + the intended destination of the connections are a Service targeted as a + ParentRef of the Route. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + When the parent resource is a Service, this targets a specific port in the + Service spec. When both Port (experimental) and SectionName are specified, + the name and port of the selected port must match both specified values. + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + SectionName is the name of a section within the target resource. In the + following resources, SectionName is interpreted as the following: + + * Gateway: Listener name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + + Implementations MAY choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName is + interpreted. + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + type: array + x-kubernetes-list-type: atomic + podTemplate: + description: |- + Optional pod template used to configure the ACME challenge solver pods + used for HTTP01 challenges. + properties: + metadata: + description: |- + ObjectMeta overrides for the pod used to solve HTTP01 challenges. + Only the 'labels' and 'annotations' fields may be set. + If labels or annotations overlap with in-built values, the values here + will override the in-built values. + properties: + annotations: + additionalProperties: + type: string + description: Annotations that should be added to the created ACME HTTP01 solver pods. + type: object + labels: + additionalProperties: + type: string + description: Labels that should be added to the created ACME HTTP01 solver pods. + type: object + type: object + spec: + description: |- + PodSpec defines overrides for the HTTP01 challenge solver pod. + Check ACMEChallengeSolverHTTP01IngressPodSpec to find out currently supported fields. + All other fields will be ignored. + properties: + affinity: + description: If specified, the pod's scheduling constraints + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and subtracting + "weight" from the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + imagePullSecrets: + description: If specified, the pod's imagePullSecrets + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector is a selector which must be true for the pod to fit on a node. + Selector which must match a node's labels for the pod to be scheduled on that node. + More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + type: object + priorityClassName: + description: If specified, the pod's priorityClassName. + type: string + resources: + description: |- + If specified, the pod's resource requirements. + These values override the global resource configuration flags. + Note that when only specifying resource limits, ensure they are greater than or equal + to the corresponding global resource requests configured via controller flags + (--acme-http01-solver-resource-request-cpu, --acme-http01-solver-resource-request-memory). + Kubernetes will reject pod creation if limits are lower than requests, causing challenge failures. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to the global values configured via controller flags. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + securityContext: + description: If specified, the pod's security context + properties: + fsGroup: + description: |- + A special supplemental group that applies to all containers in a pod. + Some volume types allow the Kubelet to change the ownership of that volume + to be owned by the pod: + + 1. The owning GID will be the FSGroup + 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) + 3. The permission bits are OR'd with rw-rw---- + + If unset, the Kubelet will not modify the ownership and permissions of any volume. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + fsGroupChangePolicy: + description: |- + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume + before being exposed inside Pod. This field will only apply to + volume types which support fsGroup based ownership(and permissions). + It will have no effect on ephemeral volume types such as: secret, configmaps + and emptydir. + Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. + Note that this field cannot be set when spec.os.name is windows. + type: string + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in SecurityContext. If set in + both SecurityContext and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies to the container. + type: string + role: + description: Role is a SELinux role label that applies to the container. + type: string + type: + description: Type is a SELinux type label that applies to the container. + type: string + user: + description: User is a SELinux user label that applies to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + supplementalGroups: + description: |- + A list of groups applied to the first process run in each container, in addition + to the container's primary GID, the fsGroup (if specified), and group memberships + defined in the container image for the uid of the container process. If unspecified, + no additional groups are added to any container. Note that group memberships + defined in the container image for the uid of the container process are still effective, + even if they are not included in this list. + Note that this field cannot be set when spec.os.name is windows. + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + sysctls: + description: |- + Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported + sysctls (by the container runtime) might fail to launch. + Note that this field cannot be set when spec.os.name is windows. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + type: object + serviceAccountName: + description: If specified, the pod's service account + type: string + tolerations: + description: If specified, the pod's tolerations. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + serviceType: + description: |- + Optional service type for Kubernetes solver service. Supported values + are NodePort or ClusterIP. If unset, defaults to NodePort. + type: string + type: object + ingress: + description: |- + The ingress based HTTP01 challenge solver will solve challenges by + creating or modifying Ingress resources in order to route requests for + '/.well-known/acme-challenge/XYZ' to 'challenge solver' pods that are + provisioned by cert-manager for each Challenge to be completed. + properties: + class: + description: |- + This field configures the annotation `kubernetes.io/ingress.class` when + creating Ingress resources to solve ACME challenges that use this + challenge solver. Only one of `class`, `name` or `ingressClassName` may + be specified. + type: string + ingressClassName: + description: |- + This field configures the field `ingressClassName` on the created Ingress + resources used to solve ACME challenges that use this challenge solver. + This is the recommended way of configuring the ingress class. Only one of + `class`, `name` or `ingressClassName` may be specified. + type: string + ingressTemplate: + description: |- + Optional ingress template used to configure the ACME challenge solver + ingress used for HTTP01 challenges. + properties: + metadata: + description: |- + ObjectMeta overrides for the ingress used to solve HTTP01 challenges. + Only the 'labels' and 'annotations' fields may be set. + If labels or annotations overlap with in-built values, the values here + will override the in-built values. + properties: + annotations: + additionalProperties: + type: string + description: Annotations that should be added to the created ACME HTTP01 solver ingress. + type: object + labels: + additionalProperties: + type: string + description: Labels that should be added to the created ACME HTTP01 solver ingress. + type: object + type: object + type: object + name: + description: |- + The name of the ingress resource that should have ACME challenge solving + routes inserted into it in order to solve HTTP01 challenges. + This is typically used in conjunction with ingress controllers like + ingress-gce, which maintains a 1:1 mapping between external IPs and + ingress resources. Only one of `class`, `name` or `ingressClassName` may + be specified. + type: string + podTemplate: + description: |- + Optional pod template used to configure the ACME challenge solver pods + used for HTTP01 challenges. + properties: + metadata: + description: |- + ObjectMeta overrides for the pod used to solve HTTP01 challenges. + Only the 'labels' and 'annotations' fields may be set. + If labels or annotations overlap with in-built values, the values here + will override the in-built values. + properties: + annotations: + additionalProperties: + type: string + description: Annotations that should be added to the created ACME HTTP01 solver pods. + type: object + labels: + additionalProperties: + type: string + description: Labels that should be added to the created ACME HTTP01 solver pods. + type: object + type: object + spec: + description: |- + PodSpec defines overrides for the HTTP01 challenge solver pod. + Check ACMEChallengeSolverHTTP01IngressPodSpec to find out currently supported fields. + All other fields will be ignored. + properties: + affinity: + description: If specified, the pod's scheduling constraints + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and subtracting + "weight" from the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + imagePullSecrets: + description: If specified, the pod's imagePullSecrets + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector is a selector which must be true for the pod to fit on a node. + Selector which must match a node's labels for the pod to be scheduled on that node. + More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + type: object + priorityClassName: + description: If specified, the pod's priorityClassName. + type: string + resources: + description: |- + If specified, the pod's resource requirements. + These values override the global resource configuration flags. + Note that when only specifying resource limits, ensure they are greater than or equal + to the corresponding global resource requests configured via controller flags + (--acme-http01-solver-resource-request-cpu, --acme-http01-solver-resource-request-memory). + Kubernetes will reject pod creation if limits are lower than requests, causing challenge failures. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to the global values configured via controller flags. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + securityContext: + description: If specified, the pod's security context + properties: + fsGroup: + description: |- + A special supplemental group that applies to all containers in a pod. + Some volume types allow the Kubelet to change the ownership of that volume + to be owned by the pod: + + 1. The owning GID will be the FSGroup + 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) + 3. The permission bits are OR'd with rw-rw---- + + If unset, the Kubelet will not modify the ownership and permissions of any volume. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + fsGroupChangePolicy: + description: |- + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume + before being exposed inside Pod. This field will only apply to + volume types which support fsGroup based ownership(and permissions). + It will have no effect on ephemeral volume types such as: secret, configmaps + and emptydir. + Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. + Note that this field cannot be set when spec.os.name is windows. + type: string + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in SecurityContext. If set in + both SecurityContext and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies to the container. + type: string + role: + description: Role is a SELinux role label that applies to the container. + type: string + type: + description: Type is a SELinux type label that applies to the container. + type: string + user: + description: User is a SELinux user label that applies to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + supplementalGroups: + description: |- + A list of groups applied to the first process run in each container, in addition + to the container's primary GID, the fsGroup (if specified), and group memberships + defined in the container image for the uid of the container process. If unspecified, + no additional groups are added to any container. Note that group memberships + defined in the container image for the uid of the container process are still effective, + even if they are not included in this list. + Note that this field cannot be set when spec.os.name is windows. + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + sysctls: + description: |- + Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported + sysctls (by the container runtime) might fail to launch. + Note that this field cannot be set when spec.os.name is windows. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + type: object + serviceAccountName: + description: If specified, the pod's service account + type: string + tolerations: + description: If specified, the pod's tolerations. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + serviceType: + description: |- + Optional service type for Kubernetes solver service. Supported values + are NodePort or ClusterIP. If unset, defaults to NodePort. + type: string + type: object + type: object + selector: + description: |- + Selector selects a set of DNSNames on the Certificate resource that + should be solved using this challenge solver. + If not specified, the solver will be treated as the 'default' solver + with the lowest priority, i.e. if any other solver has a more specific + match, it will be used instead. + properties: + dnsNames: + description: |- + List of DNSNames that this solver will be used to solve. + If specified and a match is found, a dnsNames selector will take + precedence over a dnsZones selector. + If multiple solvers match with the same dnsNames value, the solver + with the most matching labels in matchLabels will be selected. + If neither has more matches, the solver defined earlier in the list + will be selected. + items: + type: string + type: array + x-kubernetes-list-type: atomic + dnsZones: + description: |- + List of DNSZones that this solver will be used to solve. + The most specific DNS zone match specified here will take precedence + over other DNS zone matches, so a solver specifying sys.example.com + will be selected over one specifying example.com for the domain + www.sys.example.com. + If multiple solvers match with the same dnsZones value, the solver + with the most matching labels in matchLabels will be selected. + If neither has more matches, the solver defined earlier in the list + will be selected. + items: + type: string + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + A label selector that is used to refine the set of certificate's that + this challenge solver will apply to. + type: object + type: object + type: object + type: array + x-kubernetes-list-type: atomic + required: + - privateKeySecretRef + - server + type: object + ca: + description: |- + CA configures this issuer to sign certificates using a signing CA keypair + stored in a Secret resource. + This is used to build internal PKIs that are managed by cert-manager. + properties: + crlDistributionPoints: + description: |- + The CRL distribution points is an X.509 v3 certificate extension which identifies + the location of the CRL from which the revocation of this certificate can be checked. + If not set, certificates will be issued without distribution points set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + issuingCertificateURLs: + description: |- + IssuingCertificateURLs is a list of URLs which this issuer should embed into certificates + it creates. See https://www.rfc-editor.org/rfc/rfc5280#section-4.2.2.1 for more details. + As an example, such a URL might be "http://ca.domain.com/ca.crt". + items: + type: string + type: array + x-kubernetes-list-type: atomic + ocspServers: + description: |- + The OCSP server list is an X.509 v3 extension that defines a list of + URLs of OCSP responders. The OCSP responders can be queried for the + revocation status of an issued certificate. If not set, the + certificate will be issued with no OCSP servers set. For example, an + OCSP server URL could be "http://ocsp.int-x3.letsencrypt.org". + items: + type: string + type: array + x-kubernetes-list-type: atomic + secretName: + description: |- + SecretName is the name of the secret used to sign Certificates issued + by this Issuer. + type: string + required: + - secretName + type: object + selfSigned: + description: |- + SelfSigned configures this issuer to 'self sign' certificates using the + private key used to create the CertificateRequest object. + properties: + crlDistributionPoints: + description: |- + The CRL distribution points is an X.509 v3 certificate extension which identifies + the location of the CRL from which the revocation of this certificate can be checked. + If not set certificate will be issued without CDP. Values are strings. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + vault: + description: |- + Vault configures this issuer to sign certificates using a HashiCorp Vault + PKI backend. + properties: + auth: + description: Auth configures how cert-manager authenticates with the Vault server. + properties: + appRole: + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. + properties: + path: + description: |- + Path where the App Role authentication backend is mounted in Vault, e.g: + "approle" + type: string + roleId: + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. + type: string + secretRef: + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + required: + - path + - roleId + - secretRef + type: object + clientCertificate: + description: |- + ClientCertificate authenticates with Vault by presenting a client + certificate during the request's TLS handshake. + Works only when using HTTPS protocol. + properties: + mountPath: + description: |- + The Vault mountPath here is the mount path to use when authenticating with + Vault. For example, setting a value to `/v1/auth/foo`, will use the path + `/v1/auth/foo/login` to authenticate with Vault. If unspecified, the + default value "/v1/auth/cert" will be used. + type: string + name: + description: |- + Name of the certificate role to authenticate against. + If not set, matching any certificate role, if available. + type: string + secretName: + description: |- + Reference to Kubernetes Secret of type "kubernetes.io/tls" (hence containing + tls.crt and tls.key) used to authenticate to Vault using TLS client + authentication. + type: string + type: object + kubernetes: + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. + properties: + mountPath: + description: |- + The Vault mountPath here is the mount path to use when authenticating with + Vault. For example, setting a value to `/v1/auth/foo`, will use the path + `/v1/auth/foo/login` to authenticate with Vault. If unspecified, the + default value "/v1/auth/kubernetes" will be used. + type: string + role: + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. + type: string + secretRef: + description: |- + The required Secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. Use of 'ambient credentials' is not + supported. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + serviceAccountRef: + description: |- + A reference to a service account that will be used to request a bound + token (also known as "projected token"). Compared to using "secretRef", + using this field means that you don't rely on statically bound tokens. To + use this field, you must configure an RBAC rule to let cert-manager + request a token. + properties: + audiences: + description: |- + TokenAudiences is an optional list of extra audiences to include in the token passed to Vault. The default token + consisting of the issuer's namespace and name is always included. + items: + type: string + type: array + x-kubernetes-list-type: atomic + name: + description: Name of the ServiceAccount used to request a token. + type: string + required: + - name + type: object + required: + - role + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by presenting a token. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + type: object + caBundle: + description: |- + Base64-encoded bundle of PEM CAs which will be used to validate the certificate + chain presented by Vault. Only used if using HTTPS to connect to Vault and + ignored for HTTP connections. + Mutually exclusive with CABundleSecretRef. + If neither CABundle nor CABundleSecretRef are defined, the certificate bundle in + the cert-manager controller container is used to validate the TLS connection. + format: byte + type: string + caBundleSecretRef: + description: |- + Reference to a Secret containing a bundle of PEM-encoded CAs to use when + verifying the certificate chain presented by Vault when using HTTPS. + Mutually exclusive with CABundle. + If neither CABundle nor CABundleSecretRef are defined, the certificate bundle in + the cert-manager controller container is used to validate the TLS connection. + If no key for the Secret is specified, cert-manager will default to 'ca.crt'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + clientCertSecretRef: + description: |- + Reference to a Secret containing a PEM-encoded Client Certificate to use when the + Vault server requires mTLS. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + clientKeySecretRef: + description: |- + Reference to a Secret containing a PEM-encoded Client Private Key to use when the + Vault server requires mTLS. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + namespace: + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1" + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces + type: string + path: + description: |- + Path is the mount path of the Vault PKI backend's `sign` endpoint, e.g: + "my_pki_mount/sign/my-role-name". + type: string + server: + description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' + type: string + serverName: + description: |- + ServerName is used to verify the hostname on the returned certificates + by the Vault server. + type: string + required: + - auth + - path + - server + type: object + venafi: + description: |- + Venafi configures this issuer to sign certificates using a Venafi TPP + or Venafi Cloud policy zone. + properties: + cloud: + description: |- + Cloud specifies the Venafi cloud configuration settings. + Only one of TPP or Cloud may be specified. + properties: + apiTokenSecretRef: + description: APITokenSecretRef is a secret key selector for the Venafi Cloud API token. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + url: + description: |- + URL is the base URL for Venafi Cloud. + Defaults to "https://api.venafi.cloud/". + type: string + required: + - apiTokenSecretRef + type: object + tpp: + description: |- + TPP specifies Trust Protection Platform configuration settings. + Only one of TPP or Cloud may be specified. + properties: + caBundle: + description: |- + Base64-encoded bundle of PEM CAs which will be used to validate the certificate + chain presented by the TPP server. Only used if using HTTPS; ignored for HTTP. + If undefined, the certificate bundle in the cert-manager controller container + is used to validate the chain. + format: byte + type: string + caBundleSecretRef: + description: |- + Reference to a Secret containing a base64-encoded bundle of PEM CAs + which will be used to validate the certificate chain presented by the TPP server. + Only used if using HTTPS; ignored for HTTP. Mutually exclusive with CABundle. + If neither CABundle nor CABundleSecretRef is defined, the certificate bundle in + the cert-manager controller container is used to validate the TLS connection. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + credentialsRef: + description: |- + CredentialsRef is a reference to a Secret containing the Venafi TPP API credentials. + The secret must contain the key 'access-token' for the Access Token Authentication, + or two keys, 'username' and 'password' for the API Keys Authentication. + properties: + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + url: + description: |- + URL is the base URL for the vedsdk endpoint of the Venafi TPP instance, + for example: "https://tpp.example.com/vedsdk". + type: string + required: + - credentialsRef + - url + type: object + zone: + description: |- + Zone is the Venafi Policy Zone to use for this issuer. + All requests made to the Venafi platform will be restricted by the named + zone policy. + This field is required. + type: string + required: + - zone + type: object + type: object + status: + description: Status of the ClusterIssuer. This is set and managed automatically. + properties: + acme: + description: |- + ACME specific status options. + This field should only be set if the Issuer is configured to use an ACME + server to issue certificates. + properties: + lastPrivateKeyHash: + description: |- + LastPrivateKeyHash is a hash of the private key associated with the latest + registered ACME account, in order to track changes made to registered account + associated with the Issuer + type: string + lastRegisteredEmail: + description: |- + LastRegisteredEmail is the email associated with the latest registered + ACME account, in order to track changes made to registered account + associated with the Issuer + type: string + uri: + description: |- + URI is the unique account identifier, which can also be used to retrieve + account details from the CA + type: string + type: object + conditions: + description: |- + List of status conditions to indicate the status of a CertificateRequest. + Known condition types are `Ready`. + items: + description: IssuerCondition contains condition information for an Issuer. + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the timestamp corresponding to the last status + change of this condition. + format: date-time + type: string + message: + description: |- + Message is a human readable description of the details of the last + transition, complementing reason. + type: string + observedGeneration: + description: |- + If set, this represents the .metadata.generation that the condition was + set based upon. + For instance, if .metadata.generation is currently 12, but the + .status.condition[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the Issuer. + format: int64 + type: integer + reason: + description: |- + Reason is a brief machine readable explanation for the condition's last + transition. + type: string + status: + description: Status of the condition, one of (`True`, `False`, `Unknown`). + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: Type of the condition, known values are (`Ready`). + type: string + required: + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/packages/system/cert-manager-crds/templates/crd-cert-manager.io_issuers.yaml b/packages/system/cert-manager-crds/templates/crd-cert-manager.io_issuers.yaml new file mode 100644 index 00000000..43277f84 --- /dev/null +++ b/packages/system/cert-manager-crds/templates/crd-cert-manager.io_issuers.yaml @@ -0,0 +1,3814 @@ +{{- if or .Values.crds.enabled .Values.installCRDs }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: "issuers.cert-manager.io" + {{- if .Values.crds.keep }} + annotations: + helm.sh/resource-policy: keep + {{- end }} + labels: + {{- include "cert-manager.crd-labels" . | nindent 4 }} +spec: + group: cert-manager.io + names: + categories: + - cert-manager + kind: Issuer + listKind: IssuerList + plural: issuers + shortNames: + - iss + singular: issuer + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type == "Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type == "Ready")].message + name: Status + priority: 1 + type: string + - description: CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC. + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: |- + An Issuer represents a certificate issuing authority which can be + referenced as part of `issuerRef` fields. + It is scoped to a single namespace and can therefore only be referenced by + resources within the same namespace. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Desired state of the Issuer resource. + properties: + acme: + description: |- + ACME configures this issuer to communicate with a RFC8555 (ACME) server + to obtain signed x509 certificates. + properties: + caBundle: + description: |- + Base64-encoded bundle of PEM CAs which can be used to validate the certificate + chain presented by the ACME server. + Mutually exclusive with SkipTLSVerify; prefer using CABundle to prevent various + kinds of security vulnerabilities. + If CABundle and SkipTLSVerify are unset, the system certificate bundle inside + the container is used to validate the TLS connection. + format: byte + type: string + disableAccountKeyGeneration: + description: |- + Enables or disables generating a new ACME account key. + If true, the Issuer resource will *not* request a new account but will expect + the account key to be supplied via an existing secret. + If false, the cert-manager system will generate a new ACME account key + for the Issuer. + Defaults to false. + type: boolean + email: + description: |- + Email is the email address to be associated with the ACME account. + This field is optional, but it is strongly recommended to be set. + It will be used to contact you in case of issues with your account or + certificates, including expiry notification emails. + This field may be updated after the account is initially registered. + type: string + enableDurationFeature: + description: |- + Enables requesting a Not After date on certificates that matches the + duration of the certificate. This is not supported by all ACME servers + like Let's Encrypt. If set to true when the ACME server does not support + it, it will create an error on the Order. + Defaults to false. + type: boolean + externalAccountBinding: + description: |- + ExternalAccountBinding is a reference to a CA external account of the ACME + server. + If set, upon registration cert-manager will attempt to associate the given + external account credentials with the registered ACME account. + properties: + keyAlgorithm: + description: |- + Deprecated: keyAlgorithm field exists for historical compatibility + reasons and should not be used. The algorithm is now hardcoded to HS256 + in golang/x/crypto/acme. + enum: + - HS256 + - HS384 + - HS512 + type: string + keyID: + description: keyID is the ID of the CA key that the External Account is bound to. + type: string + keySecretRef: + description: |- + keySecretRef is a Secret Key Selector referencing a data item in a Kubernetes + Secret which holds the symmetric MAC key of the External Account Binding. + The `key` is the index string that is paired with the key data in the + Secret and should not be confused with the key data itself, or indeed with + the External Account Binding keyID above. + The secret key stored in the Secret **must** be un-padded, base64 URL + encoded data. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + required: + - keyID + - keySecretRef + type: object + preferredChain: + description: |- + PreferredChain is the chain to use if the ACME server outputs multiple. + PreferredChain is no guarantee that this one gets delivered by the ACME + endpoint. + For example, for Let's Encrypt's DST cross-sign you would use: + "DST Root CA X3" or "ISRG Root X1" for the newer Let's Encrypt root CA. + This value picks the first certificate bundle in the combined set of + ACME default and alternative chains that has a root-most certificate with + this value as its issuer's commonname. + maxLength: 64 + type: string + privateKeySecretRef: + description: |- + PrivateKey is the name of a Kubernetes Secret resource that will be used to + store the automatically generated ACME account private key. + Optionally, a `key` may be specified to select a specific entry within + the named Secret resource. + If `key` is not specified, a default of `tls.key` will be used. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + profile: + description: |- + Profile allows requesting a certificate profile from the ACME server. + Supported profiles are listed by the server's ACME directory URL. + type: string + server: + description: |- + Server is the URL used to access the ACME server's 'directory' endpoint. + For example, for Let's Encrypt's staging endpoint, you would use: + "https://acme-staging-v02.api.letsencrypt.org/directory". + Only ACME v2 endpoints (i.e. RFC 8555) are supported. + type: string + skipTLSVerify: + description: |- + INSECURE: Enables or disables validation of the ACME server TLS certificate. + If true, requests to the ACME server will not have the TLS certificate chain + validated. + Mutually exclusive with CABundle; prefer using CABundle to prevent various + kinds of security vulnerabilities. + Only enable this option in development environments. + If CABundle and SkipTLSVerify are unset, the system certificate bundle inside + the container is used to validate the TLS connection. + Defaults to false. + type: boolean + solvers: + description: |- + Solvers is a list of challenge solvers that will be used to solve + ACME challenges for the matching domains. + Solver configurations must be provided in order to obtain certificates + from an ACME server. + For more information, see: https://cert-manager.io/docs/configuration/acme/ + items: + description: |- + An ACMEChallengeSolver describes how to solve ACME challenges for the issuer it is part of. + A selector may be provided to use different solving strategies for different DNS names. + Only one of HTTP01 or DNS01 must be provided. + properties: + dns01: + description: |- + Configures cert-manager to attempt to complete authorizations by + performing the DNS01 challenge flow. + properties: + acmeDNS: + description: |- + Use the 'ACME DNS' (https://github.com/joohoi/acme-dns) API to manage + DNS01 challenge records. + properties: + accountSecretRef: + description: |- + A reference to a specific 'key' within a Secret resource. + In some instances, `key` is a required field. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + host: + type: string + required: + - accountSecretRef + - host + type: object + akamai: + description: Use the Akamai DNS zone management API to manage DNS01 challenge records. + properties: + accessTokenSecretRef: + description: |- + A reference to a specific 'key' within a Secret resource. + In some instances, `key` is a required field. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + clientSecretSecretRef: + description: |- + A reference to a specific 'key' within a Secret resource. + In some instances, `key` is a required field. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + clientTokenSecretRef: + description: |- + A reference to a specific 'key' within a Secret resource. + In some instances, `key` is a required field. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + serviceConsumerDomain: + type: string + required: + - accessTokenSecretRef + - clientSecretSecretRef + - clientTokenSecretRef + - serviceConsumerDomain + type: object + azureDNS: + description: Use the Microsoft Azure DNS API to manage DNS01 challenge records. + properties: + clientID: + description: |- + Auth: Azure Service Principal: + The ClientID of the Azure Service Principal used to authenticate with Azure DNS. + If set, ClientSecret and TenantID must also be set. + type: string + clientSecretSecretRef: + description: |- + Auth: Azure Service Principal: + A reference to a Secret containing the password associated with the Service Principal. + If set, ClientID and TenantID must also be set. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + environment: + description: name of the Azure environment (default AzurePublicCloud) + enum: + - AzurePublicCloud + - AzureChinaCloud + - AzureGermanCloud + - AzureUSGovernmentCloud + type: string + hostedZoneName: + description: name of the DNS zone that should be used + type: string + managedIdentity: + description: |- + Auth: Azure Workload Identity or Azure Managed Service Identity: + Settings to enable Azure Workload Identity or Azure Managed Service Identity + If set, ClientID, ClientSecret and TenantID must not be set. + properties: + clientID: + description: client ID of the managed identity, cannot be used at the same time as resourceID + type: string + resourceID: + description: |- + resource ID of the managed identity, cannot be used at the same time as clientID + Cannot be used for Azure Managed Service Identity + type: string + tenantID: + description: tenant ID of the managed identity, cannot be used at the same time as resourceID + type: string + type: object + resourceGroupName: + description: resource group the DNS zone is located in + type: string + subscriptionID: + description: ID of the Azure subscription + type: string + tenantID: + description: |- + Auth: Azure Service Principal: + The TenantID of the Azure Service Principal used to authenticate with Azure DNS. + If set, ClientID and ClientSecret must also be set. + type: string + required: + - resourceGroupName + - subscriptionID + type: object + cloudDNS: + description: Use the Google Cloud DNS API to manage DNS01 challenge records. + properties: + hostedZoneName: + description: |- + HostedZoneName is an optional field that tells cert-manager in which + Cloud DNS zone the challenge record has to be created. + If left empty cert-manager will automatically choose a zone. + type: string + project: + type: string + serviceAccountSecretRef: + description: |- + A reference to a specific 'key' within a Secret resource. + In some instances, `key` is a required field. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + required: + - project + type: object + cloudflare: + description: Use the Cloudflare API to manage DNS01 challenge records. + properties: + apiKeySecretRef: + description: |- + API key to use to authenticate with Cloudflare. + Note: using an API token to authenticate is now the recommended method + as it allows greater control of permissions. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + apiTokenSecretRef: + description: API token used to authenticate with Cloudflare. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + email: + description: Email of the account, only required when using API key based authentication. + type: string + type: object + cnameStrategy: + description: |- + CNAMEStrategy configures how the DNS01 provider should handle CNAME + records when found in DNS zones. + enum: + - None + - Follow + type: string + digitalocean: + description: Use the DigitalOcean DNS API to manage DNS01 challenge records. + properties: + tokenSecretRef: + description: |- + A reference to a specific 'key' within a Secret resource. + In some instances, `key` is a required field. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + required: + - tokenSecretRef + type: object + rfc2136: + description: |- + Use RFC2136 ("Dynamic Updates in the Domain Name System") (https://datatracker.ietf.org/doc/rfc2136/) + to manage DNS01 challenge records. + properties: + nameserver: + description: |- + The IP address or hostname of an authoritative DNS server supporting + RFC2136 in the form host:port. If the host is an IPv6 address it must be + enclosed in square brackets (e.g [2001:db8::1]) ; port is optional. + This field is required. + type: string + protocol: + description: Protocol to use for dynamic DNS update queries. Valid values are (case-sensitive) ``TCP`` and ``UDP``; ``UDP`` (default). + enum: + - TCP + - UDP + type: string + tsigAlgorithm: + description: |- + The TSIG Algorithm configured in the DNS supporting RFC2136. Used only + when ``tsigSecretSecretRef`` and ``tsigKeyName`` are defined. + Supported values are (case-insensitive): ``HMACMD5`` (default), + ``HMACSHA1``, ``HMACSHA256`` or ``HMACSHA512``. + type: string + tsigKeyName: + description: |- + The TSIG Key name configured in the DNS. + If ``tsigSecretSecretRef`` is defined, this field is required. + type: string + tsigSecretSecretRef: + description: |- + The name of the secret containing the TSIG value. + If ``tsigKeyName`` is defined, this field is required. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + required: + - nameserver + type: object + route53: + description: Use the AWS Route53 API to manage DNS01 challenge records. + properties: + accessKeyID: + description: |- + The AccessKeyID is used for authentication. + Cannot be set when SecretAccessKeyID is set. + If neither the Access Key nor Key ID are set, we fall-back to using env + vars, shared credentials file or AWS Instance metadata, + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials + type: string + accessKeyIDSecretRef: + description: |- + The SecretAccessKey is used for authentication. If set, pull the AWS + access key ID from a key within a Kubernetes Secret. + Cannot be set when AccessKeyID is set. + If neither the Access Key nor Key ID are set, we fall-back to using env + vars, shared credentials file or AWS Instance metadata, + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + auth: + description: Auth configures how cert-manager authenticates. + properties: + kubernetes: + description: |- + Kubernetes authenticates with Route53 using AssumeRoleWithWebIdentity + by passing a bound ServiceAccount token. + properties: + serviceAccountRef: + description: |- + A reference to a service account that will be used to request a bound + token (also known as "projected token"). To use this field, you must + configure an RBAC rule to let cert-manager request a token. + properties: + audiences: + description: |- + TokenAudiences is an optional list of audiences to include in the + token passed to AWS. The default token consisting of the issuer's namespace + and name is always included. + If unset the audience defaults to `sts.amazonaws.com`. + items: + type: string + type: array + x-kubernetes-list-type: atomic + name: + description: Name of the ServiceAccount used to request a token. + type: string + required: + - name + type: object + required: + - serviceAccountRef + type: object + required: + - kubernetes + type: object + hostedZoneID: + description: If set, the provider will manage only this zone in Route53 and will not do a lookup using the route53:ListHostedZonesByName api call. + type: string + region: + description: |- + Override the AWS region. + + Route53 is a global service and does not have regional endpoints but the + region specified here (or via environment variables) is used as a hint to + help compute the correct AWS credential scope and partition when it + connects to Route53. See: + - [Amazon Route 53 endpoints and quotas](https://docs.aws.amazon.com/general/latest/gr/r53.html) + - [Global services](https://docs.aws.amazon.com/whitepapers/latest/aws-fault-isolation-boundaries/global-services.html) + + If you omit this region field, cert-manager will use the region from + AWS_REGION and AWS_DEFAULT_REGION environment variables, if they are set + in the cert-manager controller Pod. + + The `region` field is not needed if you use [IAM Roles for Service Accounts (IRSA)](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html). + Instead an AWS_REGION environment variable is added to the cert-manager controller Pod by: + [Amazon EKS Pod Identity Webhook](https://github.com/aws/amazon-eks-pod-identity-webhook). + In this case this `region` field value is ignored. + + The `region` field is not needed if you use [EKS Pod Identities](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html). + Instead an AWS_REGION environment variable is added to the cert-manager controller Pod by: + [Amazon EKS Pod Identity Agent](https://github.com/aws/eks-pod-identity-agent), + In this case this `region` field value is ignored. + type: string + role: + description: |- + Role is a Role ARN which the Route53 provider will assume using either the explicit credentials AccessKeyID/SecretAccessKey + or the inferred credentials from environment variables, shared credentials file or AWS Instance metadata + type: string + secretAccessKeySecretRef: + description: |- + The SecretAccessKey is used for authentication. + If neither the Access Key nor Key ID are set, we fall-back to using env + vars, shared credentials file or AWS Instance metadata, + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + type: object + webhook: + description: |- + Configure an external webhook based DNS01 challenge solver to manage + DNS01 challenge records. + properties: + config: + description: |- + Additional configuration that should be passed to the webhook apiserver + when challenges are processed. + This can contain arbitrary JSON data. + Secret values should not be specified in this stanza. + If secret values are needed (e.g., credentials for a DNS service), you + should use a SecretKeySelector to reference a Secret resource. + For details on the schema of this field, consult the webhook provider + implementation's documentation. + x-kubernetes-preserve-unknown-fields: true + groupName: + description: |- + The API group name that should be used when POSTing ChallengePayload + resources to the webhook apiserver. + This should be the same as the GroupName specified in the webhook + provider implementation. + type: string + solverName: + description: |- + The name of the solver to use, as defined in the webhook provider + implementation. + This will typically be the name of the provider, e.g., 'cloudflare'. + type: string + required: + - groupName + - solverName + type: object + type: object + http01: + description: |- + Configures cert-manager to attempt to complete authorizations by + performing the HTTP01 challenge flow. + It is not possible to obtain certificates for wildcard domain names + (e.g., `*.example.com`) using the HTTP01 challenge mechanism. + properties: + gatewayHTTPRoute: + description: |- + The Gateway API is a sig-network community API that models service networking + in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will + create HTTPRoutes with the specified labels in the same namespace as the challenge. + This solver is experimental, and fields / behaviour may change in the future. + properties: + labels: + additionalProperties: + type: string + description: |- + Custom labels that will be applied to HTTPRoutes created by cert-manager + while solving HTTP-01 challenges. + type: object + parentRefs: + description: |- + When solving an HTTP-01 challenge, cert-manager creates an HTTPRoute. + cert-manager needs to know which parentRefs should be used when creating + the HTTPRoute. Usually, the parentRef references a Gateway. See: + https://gateway-api.sigs.k8s.io/api-types/httproute/#attaching-to-gateways + items: + description: |- + ParentReference identifies an API object (usually a Gateway) that can be considered + a parent of this resource (usually a route). There are two kinds of parent resources + with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + This API may be extended in the future to support additional kinds of parent + resources. + + The API object must be valid in the cluster; the Group and Kind must + be registered in the cluster for this reference to be valid. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + ParentRefs from a Route to a Service in the same namespace are "producer" + routes, which apply default routing rules to inbound connections from + any namespace to the Service. + + ParentRefs from a Route to a Service in a different namespace are + "consumer" routes, and these routing rules are only applied to outbound + connections originating from the same namespace as the Route, for which + the intended destination of the connections are a Service targeted as a + ParentRef of the Route. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + When the parent resource is a Service, this targets a specific port in the + Service spec. When both Port (experimental) and SectionName are specified, + the name and port of the selected port must match both specified values. + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + SectionName is the name of a section within the target resource. In the + following resources, SectionName is interpreted as the following: + + * Gateway: Listener name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + + Implementations MAY choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName is + interpreted. + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + type: array + x-kubernetes-list-type: atomic + podTemplate: + description: |- + Optional pod template used to configure the ACME challenge solver pods + used for HTTP01 challenges. + properties: + metadata: + description: |- + ObjectMeta overrides for the pod used to solve HTTP01 challenges. + Only the 'labels' and 'annotations' fields may be set. + If labels or annotations overlap with in-built values, the values here + will override the in-built values. + properties: + annotations: + additionalProperties: + type: string + description: Annotations that should be added to the created ACME HTTP01 solver pods. + type: object + labels: + additionalProperties: + type: string + description: Labels that should be added to the created ACME HTTP01 solver pods. + type: object + type: object + spec: + description: |- + PodSpec defines overrides for the HTTP01 challenge solver pod. + Check ACMEChallengeSolverHTTP01IngressPodSpec to find out currently supported fields. + All other fields will be ignored. + properties: + affinity: + description: If specified, the pod's scheduling constraints + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and subtracting + "weight" from the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + imagePullSecrets: + description: If specified, the pod's imagePullSecrets + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector is a selector which must be true for the pod to fit on a node. + Selector which must match a node's labels for the pod to be scheduled on that node. + More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + type: object + priorityClassName: + description: If specified, the pod's priorityClassName. + type: string + resources: + description: |- + If specified, the pod's resource requirements. + These values override the global resource configuration flags. + Note that when only specifying resource limits, ensure they are greater than or equal + to the corresponding global resource requests configured via controller flags + (--acme-http01-solver-resource-request-cpu, --acme-http01-solver-resource-request-memory). + Kubernetes will reject pod creation if limits are lower than requests, causing challenge failures. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to the global values configured via controller flags. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + securityContext: + description: If specified, the pod's security context + properties: + fsGroup: + description: |- + A special supplemental group that applies to all containers in a pod. + Some volume types allow the Kubelet to change the ownership of that volume + to be owned by the pod: + + 1. The owning GID will be the FSGroup + 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) + 3. The permission bits are OR'd with rw-rw---- + + If unset, the Kubelet will not modify the ownership and permissions of any volume. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + fsGroupChangePolicy: + description: |- + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume + before being exposed inside Pod. This field will only apply to + volume types which support fsGroup based ownership(and permissions). + It will have no effect on ephemeral volume types such as: secret, configmaps + and emptydir. + Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. + Note that this field cannot be set when spec.os.name is windows. + type: string + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in SecurityContext. If set in + both SecurityContext and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies to the container. + type: string + role: + description: Role is a SELinux role label that applies to the container. + type: string + type: + description: Type is a SELinux type label that applies to the container. + type: string + user: + description: User is a SELinux user label that applies to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + supplementalGroups: + description: |- + A list of groups applied to the first process run in each container, in addition + to the container's primary GID, the fsGroup (if specified), and group memberships + defined in the container image for the uid of the container process. If unspecified, + no additional groups are added to any container. Note that group memberships + defined in the container image for the uid of the container process are still effective, + even if they are not included in this list. + Note that this field cannot be set when spec.os.name is windows. + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + sysctls: + description: |- + Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported + sysctls (by the container runtime) might fail to launch. + Note that this field cannot be set when spec.os.name is windows. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + type: object + serviceAccountName: + description: If specified, the pod's service account + type: string + tolerations: + description: If specified, the pod's tolerations. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + serviceType: + description: |- + Optional service type for Kubernetes solver service. Supported values + are NodePort or ClusterIP. If unset, defaults to NodePort. + type: string + type: object + ingress: + description: |- + The ingress based HTTP01 challenge solver will solve challenges by + creating or modifying Ingress resources in order to route requests for + '/.well-known/acme-challenge/XYZ' to 'challenge solver' pods that are + provisioned by cert-manager for each Challenge to be completed. + properties: + class: + description: |- + This field configures the annotation `kubernetes.io/ingress.class` when + creating Ingress resources to solve ACME challenges that use this + challenge solver. Only one of `class`, `name` or `ingressClassName` may + be specified. + type: string + ingressClassName: + description: |- + This field configures the field `ingressClassName` on the created Ingress + resources used to solve ACME challenges that use this challenge solver. + This is the recommended way of configuring the ingress class. Only one of + `class`, `name` or `ingressClassName` may be specified. + type: string + ingressTemplate: + description: |- + Optional ingress template used to configure the ACME challenge solver + ingress used for HTTP01 challenges. + properties: + metadata: + description: |- + ObjectMeta overrides for the ingress used to solve HTTP01 challenges. + Only the 'labels' and 'annotations' fields may be set. + If labels or annotations overlap with in-built values, the values here + will override the in-built values. + properties: + annotations: + additionalProperties: + type: string + description: Annotations that should be added to the created ACME HTTP01 solver ingress. + type: object + labels: + additionalProperties: + type: string + description: Labels that should be added to the created ACME HTTP01 solver ingress. + type: object + type: object + type: object + name: + description: |- + The name of the ingress resource that should have ACME challenge solving + routes inserted into it in order to solve HTTP01 challenges. + This is typically used in conjunction with ingress controllers like + ingress-gce, which maintains a 1:1 mapping between external IPs and + ingress resources. Only one of `class`, `name` or `ingressClassName` may + be specified. + type: string + podTemplate: + description: |- + Optional pod template used to configure the ACME challenge solver pods + used for HTTP01 challenges. + properties: + metadata: + description: |- + ObjectMeta overrides for the pod used to solve HTTP01 challenges. + Only the 'labels' and 'annotations' fields may be set. + If labels or annotations overlap with in-built values, the values here + will override the in-built values. + properties: + annotations: + additionalProperties: + type: string + description: Annotations that should be added to the created ACME HTTP01 solver pods. + type: object + labels: + additionalProperties: + type: string + description: Labels that should be added to the created ACME HTTP01 solver pods. + type: object + type: object + spec: + description: |- + PodSpec defines overrides for the HTTP01 challenge solver pod. + Check ACMEChallengeSolverHTTP01IngressPodSpec to find out currently supported fields. + All other fields will be ignored. + properties: + affinity: + description: If specified, the pod's scheduling constraints + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and subtracting + "weight" from the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + imagePullSecrets: + description: If specified, the pod's imagePullSecrets + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector is a selector which must be true for the pod to fit on a node. + Selector which must match a node's labels for the pod to be scheduled on that node. + More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + type: object + priorityClassName: + description: If specified, the pod's priorityClassName. + type: string + resources: + description: |- + If specified, the pod's resource requirements. + These values override the global resource configuration flags. + Note that when only specifying resource limits, ensure they are greater than or equal + to the corresponding global resource requests configured via controller flags + (--acme-http01-solver-resource-request-cpu, --acme-http01-solver-resource-request-memory). + Kubernetes will reject pod creation if limits are lower than requests, causing challenge failures. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to the global values configured via controller flags. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + securityContext: + description: If specified, the pod's security context + properties: + fsGroup: + description: |- + A special supplemental group that applies to all containers in a pod. + Some volume types allow the Kubelet to change the ownership of that volume + to be owned by the pod: + + 1. The owning GID will be the FSGroup + 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) + 3. The permission bits are OR'd with rw-rw---- + + If unset, the Kubelet will not modify the ownership and permissions of any volume. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + fsGroupChangePolicy: + description: |- + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume + before being exposed inside Pod. This field will only apply to + volume types which support fsGroup based ownership(and permissions). + It will have no effect on ephemeral volume types such as: secret, configmaps + and emptydir. + Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. + Note that this field cannot be set when spec.os.name is windows. + type: string + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in SecurityContext. If set in + both SecurityContext and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies to the container. + type: string + role: + description: Role is a SELinux role label that applies to the container. + type: string + type: + description: Type is a SELinux type label that applies to the container. + type: string + user: + description: User is a SELinux user label that applies to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + supplementalGroups: + description: |- + A list of groups applied to the first process run in each container, in addition + to the container's primary GID, the fsGroup (if specified), and group memberships + defined in the container image for the uid of the container process. If unspecified, + no additional groups are added to any container. Note that group memberships + defined in the container image for the uid of the container process are still effective, + even if they are not included in this list. + Note that this field cannot be set when spec.os.name is windows. + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + sysctls: + description: |- + Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported + sysctls (by the container runtime) might fail to launch. + Note that this field cannot be set when spec.os.name is windows. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + type: object + serviceAccountName: + description: If specified, the pod's service account + type: string + tolerations: + description: If specified, the pod's tolerations. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + serviceType: + description: |- + Optional service type for Kubernetes solver service. Supported values + are NodePort or ClusterIP. If unset, defaults to NodePort. + type: string + type: object + type: object + selector: + description: |- + Selector selects a set of DNSNames on the Certificate resource that + should be solved using this challenge solver. + If not specified, the solver will be treated as the 'default' solver + with the lowest priority, i.e. if any other solver has a more specific + match, it will be used instead. + properties: + dnsNames: + description: |- + List of DNSNames that this solver will be used to solve. + If specified and a match is found, a dnsNames selector will take + precedence over a dnsZones selector. + If multiple solvers match with the same dnsNames value, the solver + with the most matching labels in matchLabels will be selected. + If neither has more matches, the solver defined earlier in the list + will be selected. + items: + type: string + type: array + x-kubernetes-list-type: atomic + dnsZones: + description: |- + List of DNSZones that this solver will be used to solve. + The most specific DNS zone match specified here will take precedence + over other DNS zone matches, so a solver specifying sys.example.com + will be selected over one specifying example.com for the domain + www.sys.example.com. + If multiple solvers match with the same dnsZones value, the solver + with the most matching labels in matchLabels will be selected. + If neither has more matches, the solver defined earlier in the list + will be selected. + items: + type: string + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + A label selector that is used to refine the set of certificate's that + this challenge solver will apply to. + type: object + type: object + type: object + type: array + x-kubernetes-list-type: atomic + required: + - privateKeySecretRef + - server + type: object + ca: + description: |- + CA configures this issuer to sign certificates using a signing CA keypair + stored in a Secret resource. + This is used to build internal PKIs that are managed by cert-manager. + properties: + crlDistributionPoints: + description: |- + The CRL distribution points is an X.509 v3 certificate extension which identifies + the location of the CRL from which the revocation of this certificate can be checked. + If not set, certificates will be issued without distribution points set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + issuingCertificateURLs: + description: |- + IssuingCertificateURLs is a list of URLs which this issuer should embed into certificates + it creates. See https://www.rfc-editor.org/rfc/rfc5280#section-4.2.2.1 for more details. + As an example, such a URL might be "http://ca.domain.com/ca.crt". + items: + type: string + type: array + x-kubernetes-list-type: atomic + ocspServers: + description: |- + The OCSP server list is an X.509 v3 extension that defines a list of + URLs of OCSP responders. The OCSP responders can be queried for the + revocation status of an issued certificate. If not set, the + certificate will be issued with no OCSP servers set. For example, an + OCSP server URL could be "http://ocsp.int-x3.letsencrypt.org". + items: + type: string + type: array + x-kubernetes-list-type: atomic + secretName: + description: |- + SecretName is the name of the secret used to sign Certificates issued + by this Issuer. + type: string + required: + - secretName + type: object + selfSigned: + description: |- + SelfSigned configures this issuer to 'self sign' certificates using the + private key used to create the CertificateRequest object. + properties: + crlDistributionPoints: + description: |- + The CRL distribution points is an X.509 v3 certificate extension which identifies + the location of the CRL from which the revocation of this certificate can be checked. + If not set certificate will be issued without CDP. Values are strings. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + vault: + description: |- + Vault configures this issuer to sign certificates using a HashiCorp Vault + PKI backend. + properties: + auth: + description: Auth configures how cert-manager authenticates with the Vault server. + properties: + appRole: + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. + properties: + path: + description: |- + Path where the App Role authentication backend is mounted in Vault, e.g: + "approle" + type: string + roleId: + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. + type: string + secretRef: + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + required: + - path + - roleId + - secretRef + type: object + clientCertificate: + description: |- + ClientCertificate authenticates with Vault by presenting a client + certificate during the request's TLS handshake. + Works only when using HTTPS protocol. + properties: + mountPath: + description: |- + The Vault mountPath here is the mount path to use when authenticating with + Vault. For example, setting a value to `/v1/auth/foo`, will use the path + `/v1/auth/foo/login` to authenticate with Vault. If unspecified, the + default value "/v1/auth/cert" will be used. + type: string + name: + description: |- + Name of the certificate role to authenticate against. + If not set, matching any certificate role, if available. + type: string + secretName: + description: |- + Reference to Kubernetes Secret of type "kubernetes.io/tls" (hence containing + tls.crt and tls.key) used to authenticate to Vault using TLS client + authentication. + type: string + type: object + kubernetes: + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. + properties: + mountPath: + description: |- + The Vault mountPath here is the mount path to use when authenticating with + Vault. For example, setting a value to `/v1/auth/foo`, will use the path + `/v1/auth/foo/login` to authenticate with Vault. If unspecified, the + default value "/v1/auth/kubernetes" will be used. + type: string + role: + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. + type: string + secretRef: + description: |- + The required Secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. Use of 'ambient credentials' is not + supported. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + serviceAccountRef: + description: |- + A reference to a service account that will be used to request a bound + token (also known as "projected token"). Compared to using "secretRef", + using this field means that you don't rely on statically bound tokens. To + use this field, you must configure an RBAC rule to let cert-manager + request a token. + properties: + audiences: + description: |- + TokenAudiences is an optional list of extra audiences to include in the token passed to Vault. The default token + consisting of the issuer's namespace and name is always included. + items: + type: string + type: array + x-kubernetes-list-type: atomic + name: + description: Name of the ServiceAccount used to request a token. + type: string + required: + - name + type: object + required: + - role + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by presenting a token. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + type: object + caBundle: + description: |- + Base64-encoded bundle of PEM CAs which will be used to validate the certificate + chain presented by Vault. Only used if using HTTPS to connect to Vault and + ignored for HTTP connections. + Mutually exclusive with CABundleSecretRef. + If neither CABundle nor CABundleSecretRef are defined, the certificate bundle in + the cert-manager controller container is used to validate the TLS connection. + format: byte + type: string + caBundleSecretRef: + description: |- + Reference to a Secret containing a bundle of PEM-encoded CAs to use when + verifying the certificate chain presented by Vault when using HTTPS. + Mutually exclusive with CABundle. + If neither CABundle nor CABundleSecretRef are defined, the certificate bundle in + the cert-manager controller container is used to validate the TLS connection. + If no key for the Secret is specified, cert-manager will default to 'ca.crt'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + clientCertSecretRef: + description: |- + Reference to a Secret containing a PEM-encoded Client Certificate to use when the + Vault server requires mTLS. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + clientKeySecretRef: + description: |- + Reference to a Secret containing a PEM-encoded Client Private Key to use when the + Vault server requires mTLS. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + namespace: + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1" + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces + type: string + path: + description: |- + Path is the mount path of the Vault PKI backend's `sign` endpoint, e.g: + "my_pki_mount/sign/my-role-name". + type: string + server: + description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' + type: string + serverName: + description: |- + ServerName is used to verify the hostname on the returned certificates + by the Vault server. + type: string + required: + - auth + - path + - server + type: object + venafi: + description: |- + Venafi configures this issuer to sign certificates using a Venafi TPP + or Venafi Cloud policy zone. + properties: + cloud: + description: |- + Cloud specifies the Venafi cloud configuration settings. + Only one of TPP or Cloud may be specified. + properties: + apiTokenSecretRef: + description: APITokenSecretRef is a secret key selector for the Venafi Cloud API token. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + url: + description: |- + URL is the base URL for Venafi Cloud. + Defaults to "https://api.venafi.cloud/". + type: string + required: + - apiTokenSecretRef + type: object + tpp: + description: |- + TPP specifies Trust Protection Platform configuration settings. + Only one of TPP or Cloud may be specified. + properties: + caBundle: + description: |- + Base64-encoded bundle of PEM CAs which will be used to validate the certificate + chain presented by the TPP server. Only used if using HTTPS; ignored for HTTP. + If undefined, the certificate bundle in the cert-manager controller container + is used to validate the chain. + format: byte + type: string + caBundleSecretRef: + description: |- + Reference to a Secret containing a base64-encoded bundle of PEM CAs + which will be used to validate the certificate chain presented by the TPP server. + Only used if using HTTPS; ignored for HTTP. Mutually exclusive with CABundle. + If neither CABundle nor CABundleSecretRef is defined, the certificate bundle in + the cert-manager controller container is used to validate the TLS connection. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. + Some instances of this field may be defaulted, in others it may be + required. + type: string + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + credentialsRef: + description: |- + CredentialsRef is a reference to a Secret containing the Venafi TPP API credentials. + The secret must contain the key 'access-token' for the Access Token Authentication, + or two keys, 'username' and 'password' for the API Keys Authentication. + properties: + name: + description: |- + Name of the resource being referred to. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + url: + description: |- + URL is the base URL for the vedsdk endpoint of the Venafi TPP instance, + for example: "https://tpp.example.com/vedsdk". + type: string + required: + - credentialsRef + - url + type: object + zone: + description: |- + Zone is the Venafi Policy Zone to use for this issuer. + All requests made to the Venafi platform will be restricted by the named + zone policy. + This field is required. + type: string + required: + - zone + type: object + type: object + status: + description: Status of the Issuer. This is set and managed automatically. + properties: + acme: + description: |- + ACME specific status options. + This field should only be set if the Issuer is configured to use an ACME + server to issue certificates. + properties: + lastPrivateKeyHash: + description: |- + LastPrivateKeyHash is a hash of the private key associated with the latest + registered ACME account, in order to track changes made to registered account + associated with the Issuer + type: string + lastRegisteredEmail: + description: |- + LastRegisteredEmail is the email associated with the latest registered + ACME account, in order to track changes made to registered account + associated with the Issuer + type: string + uri: + description: |- + URI is the unique account identifier, which can also be used to retrieve + account details from the CA + type: string + type: object + conditions: + description: |- + List of status conditions to indicate the status of a CertificateRequest. + Known condition types are `Ready`. + items: + description: IssuerCondition contains condition information for an Issuer. + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the timestamp corresponding to the last status + change of this condition. + format: date-time + type: string + message: + description: |- + Message is a human readable description of the details of the last + transition, complementing reason. + type: string + observedGeneration: + description: |- + If set, this represents the .metadata.generation that the condition was + set based upon. + For instance, if .metadata.generation is currently 12, but the + .status.condition[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the Issuer. + format: int64 + type: integer + reason: + description: |- + Reason is a brief machine readable explanation for the condition's last + transition. + type: string + status: + description: Status of the condition, one of (`True`, `False`, `Unknown`). + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: Type of the condition, known values are (`Ready`). + type: string + required: + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/packages/system/cert-manager-crds/values.yaml b/packages/system/cert-manager-crds/values.yaml index 0b21fc93..15136853 100644 --- a/packages/system/cert-manager-crds/values.yaml +++ b/packages/system/cert-manager-crds/values.yaml @@ -1,2 +1,8 @@ -cert-manager: - installCRDs: true +global: + commonLabels: {} + +creator: helm + +crds: + enabled: true + keep: true diff --git a/packages/system/cert-manager-issuers/templates/cluster-issuers.yaml b/packages/system/cert-manager-issuers/templates/cluster-issuers.yaml index e93f3f67..3b582082 100644 --- a/packages/system/cert-manager-issuers/templates/cluster-issuers.yaml +++ b/packages/system/cert-manager-issuers/templates/cluster-issuers.yaml @@ -1,6 +1,7 @@ -{{- $issuerType := (index .Values._cluster "clusterissuer") | default "http01" }} +{{- $solver := (index .Values._cluster "solver") | default "http01" }} +{{- $exposeIngress := (index .Values._cluster "expose-ingress") | default "tenant-root" }} -apiVersion: cert-manager.io/v1 +apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt-prod @@ -10,16 +11,16 @@ spec: name: letsencrypt-prod server: https://acme-v02.api.letsencrypt.org/directory solvers: - - {{- if eq $issuerType "cloudflare" }} + - {{- if eq $solver "dns01" }} dns01: cloudflare: apiTokenSecretRef: name: cloudflare-api-token-secret key: api-token {{- else }} - http01: - ingress: - class: nginx + http01: + ingress: + ingressClassName: {{ $exposeIngress }} {{- end }} --- @@ -34,16 +35,16 @@ spec: name: letsencrypt-stage server: https://acme-staging-v02.api.letsencrypt.org/directory solvers: - - {{- if eq $issuerType "cloudflare" }} + - {{- if eq $solver "dns01" }} dns01: cloudflare: apiTokenSecretRef: name: cloudflare-api-token-secret key: api-token {{- else }} - http01: - ingress: - class: nginx + http01: + ingress: + ingressClassName: {{ $exposeIngress }} {{- end }} --- diff --git a/packages/system/cert-manager/Makefile b/packages/system/cert-manager/Makefile index 2d256086..c3ff574b 100644 --- a/packages/system/cert-manager/Makefile +++ b/packages/system/cert-manager/Makefile @@ -8,3 +8,6 @@ update: helm repo add jetstack https://charts.jetstack.io helm repo update jetstack helm pull jetstack/cert-manager --untar --untardir charts + rm -rf ../cert-manager-crds/templates/* + mv charts/cert-manager/templates/crd-*.yaml ../cert-manager-crds/templates/ + cp charts/cert-manager/templates/_helpers.tpl ../cert-manager-crds/templates/ diff --git a/packages/system/cert-manager/charts/cert-manager/Chart.yaml b/packages/system/cert-manager/charts/cert-manager/Chart.yaml index 2a49d00c..2756247d 100644 --- a/packages/system/cert-manager/charts/cert-manager/Chart.yaml +++ b/packages/system/cert-manager/charts/cert-manager/Chart.yaml @@ -6,7 +6,7 @@ annotations: fingerprint: 1020CF3C033D4F35BAE1C19E1226061C665DF13E url: https://cert-manager.io/public-keys/cert-manager-keyring-2021-09-20-1020CF3C033D4F35BAE1C19E1226061C665DF13E.gpg apiVersion: v2 -appVersion: v1.17.2 +appVersion: v1.19.3 description: A Helm chart for cert-manager home: https://cert-manager.io icon: https://raw.githubusercontent.com/cert-manager/community/4d35a69437d21b76322157e6284be4cd64e6d2b7/logo/logo-small.png @@ -23,4 +23,4 @@ maintainers: name: cert-manager sources: - https://github.com/cert-manager/cert-manager -version: v1.17.2 +version: v1.19.3 diff --git a/packages/system/cert-manager/charts/cert-manager/README.md b/packages/system/cert-manager/charts/cert-manager/README.md index 1d502429..456e740d 100644 --- a/packages/system/cert-manager/charts/cert-manager/README.md +++ b/packages/system/cert-manager/charts/cert-manager/README.md @@ -1,10 +1,10 @@ # cert-manager -cert-manager is a Kubernetes addon to automate the management and issuance of -TLS certificates from various issuing sources. +cert-manager creates TLS certificates for workloads in your Kubernetes or OpenShift cluster and renews the certificates before they expire. -It will ensure certificates are valid and up to date periodically, and attempt -to renew certificates at an appropriate time before expiry. +cert-manager can obtain certificates from a [variety of certificate authorities](https://cert-manager.io/docs/configuration/issuers/), including: +[Let's Encrypt](https://cert-manager.io/docs/configuration/acme/), [HashiCorp Vault](https://cert-manager.io/docs/configuration/vault/), +[Venafi](https://cert-manager.io/docs/configuration/venafi/) and [private PKI](https://cert-manager.io/docs/configuration/ca/). ## Prerequisites @@ -13,23 +13,21 @@ to renew certificates at an appropriate time before expiry. ## Installing the Chart Full installation instructions, including details on how to configure extra -functionality in cert-manager can be found in the [installation docs](https://cert-manager.io/docs/installation/kubernetes/). - -Before installing the chart, you must first install the cert-manager CustomResourceDefinition resources. -This is performed in a separate step to allow you to easily uninstall and reinstall cert-manager without deleting your installed custom resources. - -```bash -$ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.17.2/cert-manager.crds.yaml -``` +functionality in cert-manager can be found in the [installation docs](https://cert-manager.io/docs/installation/helm/). To install the chart with the release name `cert-manager`: ```console -## Add the Jetstack Helm repository -$ helm repo add jetstack https://charts.jetstack.io --force-update +# Add the Jetstack Helm repository +helm repo add jetstack https://charts.jetstack.io --force-update -## Install the cert-manager helm chart -$ helm install cert-manager --namespace cert-manager --version v1.17.2 jetstack/cert-manager +# Install the cert-manager helm chart +helm install \ + cert-manager jetstack/cert-manager \ + --namespace cert-manager \ + --create-namespace \ + --version v1.19.3 \ + --set crds.enabled=true ``` In order to begin issuing certificates, you will need to set up a ClusterIssuer @@ -56,17 +54,25 @@ are documented in our full [upgrading guide](https://cert-manager.io/docs/instal To uninstall/delete the `cert-manager` deployment: ```console -$ helm delete cert-manager --namespace cert-manager +helm delete cert-manager --namespace cert-manager ``` The command removes all the Kubernetes components associated with the chart and deletes the release. If you want to completely uninstall cert-manager from your cluster, you will also need to -delete the previously installed CustomResourceDefinition resources: +delete the previously installed CustomResourceDefinition resources. -```console -$ kubectl delete -f https://github.com/cert-manager/cert-manager/releases/download/v1.17.2/cert-manager.crds.yaml -``` +> ☢️ This will remove all `Issuer`,`ClusterIssuer`,`Certificate`,`CertificateRequest`,`Order` and `Challenge` resources from the cluster: +> +> ```console +> kubectl delete crd \ +> issuers.cert-manager.io \ +> clusterissuers.cert-manager.io \ +> certificates.cert-manager.io \ +> certificaterequests.cert-manager.io \ +> orders.acme.cert-manager.io \ +> challenges.acme.cert-manager.io +> ``` ## Configuration @@ -87,6 +93,18 @@ For example: imagePullSecrets: - name: "image-pull-secret" ``` +#### **global.nodeSelector** ~ `object` +> Default value: +> ```yaml +> {} +> ``` + +Global node selector + +The nodeSelector on Pods tells Kubernetes to schedule Pods on the nodes with matching labels. For more information, see [Assigning Pods to Nodes](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). + +If a component-specific nodeSelector is also set, it will be merged and take precedence. + #### **global.commonLabels** ~ `object` > Default value: > ```yaml @@ -108,6 +126,18 @@ The number of old ReplicaSets to retain to allow rollback (if not set, the defau > ``` The optional priority class to be used for the cert-manager pods. +#### **global.hostUsers** ~ `bool` + +Set all pods to run in a user namespace without host access. Experimental: may be removed once the Kubernetes User Namespaces feature is GA. + +Requirements: + - Kubernetes ≥ 1.33, or + - Kubernetes 1.27–1.32 with UserNamespacesSupport feature gate enabled. + +Set to false to run pods in a user namespace without host access. + +See [limitations](https://kubernetes.io/docs/concepts/workloads/pods/user-namespaces/#limitations) for details. + #### **global.rbac.create** ~ `bool` > Default value: > ```yaml @@ -230,13 +260,13 @@ This prevents downtime during voluntary disruptions such as during a Node upgrad Pod is currently running. #### **podDisruptionBudget.minAvailable** ~ `unknown` -This configures the minimum available pods for disruptions. It can either be set to an integer (e.g. 1) or a percentage value (e.g. 25%). +This configures the minimum available pods for disruptions. It can either be set to an integer (e.g., 1) or a percentage value (e.g., 25%). It cannot be used if `maxUnavailable` is set. #### **podDisruptionBudget.maxUnavailable** ~ `unknown` -This configures the maximum unavailable pods for disruptions. It can either be set to an integer (e.g. 1) or a percentage value (e.g. 25%). it cannot be used if `minAvailable` is set. +This configures the maximum unavailable pods for disruptions. It can either be set to an integer (e.g., 1) or a percentage value (e.g., 25%). it cannot be used if `minAvailable` is set. #### **featureGates** ~ `string` @@ -300,7 +330,7 @@ Override the "cert-manager.fullname" value. This value is used as part of most o #### **nameOverride** ~ `string` -Override the "cert-manager.name" value, which is used to annotate some of the resources that are created by this Chart (using "app.kubernetes.io/name"). NOTE: There are some inconsistencies in the Helm chart when it comes to these annotations (some resources use eg. "cainjector.name" which resolves to the value "cainjector"). +Override the "cert-manager.name" value, which is used to annotate some of the resources that are created by this Chart (using "app.kubernetes.io/name"). NOTE: There are some inconsistencies in the Helm chart when it comes to these annotations (some resources use, e.g., "cainjector.name" which resolves to the value "cainjector"). #### **serviceAccount.create** ~ `bool` > Default value: @@ -371,10 +401,10 @@ config: kubernetesAPIBurst: 9000 numberOfConcurrentWorkers: 200 enableGatewayAPI: true - # Feature gates as of v1.17.0. Listed with their default values. + # Feature gates as of v1.18.1. Listed with their default values. # See https://cert-manager.io/docs/cli/controller/ featureGates: - AdditionalCertificateOutputFormats: true # BETA - default=true + AdditionalCertificateOutputFormats: true # GA - default=true AllAlpha: false # ALPHA - default=false AllBeta: false # BETA - default=false ExperimentalCertificateSigningRequestControllers: false # ALPHA - default=false @@ -386,8 +416,10 @@ config: ServerSideApply: false # ALPHA - default=false StableCertificateRequestName: true # BETA - default=true UseCertificateRequestBasicConstraints: false # ALPHA - default=false - UseDomainQualifiedFinalizer: true # BETA - default=false + UseDomainQualifiedFinalizer: true # GA - default=true ValidateCAA: false # ALPHA - default=false + DefaultPrivateKeyRotationPolicyAlways: true # BETA - default=true + ACMEHTTP01IngressPathTypeExact: true # BETA - default=true # Configure the metrics server for TLS # See https://cert-manager.io/docs/devops-tips/prometheus-metrics/#tls metricsTLSConfig: @@ -425,7 +457,7 @@ Option to disable cert-manager's build-in auto-approver. The auto-approver appro > - clusterissuers.cert-manager.io/* > ``` -List of signer names that cert-manager will approve by default. CertificateRequests referencing these signer names will be auto-approved by cert-manager. Defaults to just approving the cert-manager.io Issuer and ClusterIssuer issuers. When set to an empty array, ALL issuers will be auto-approved by cert-manager. To disable the auto-approval, because eg. you are using approver-policy, you can enable 'disableAutoApproval'. +List of signer names that cert-manager will approve by default. CertificateRequests referencing these signer names will be auto-approved by cert-manager. Defaults to just approving the cert-manager.io Issuer and ClusterIssuer issuers. When set to an empty array, ALL issuers will be auto-approved by cert-manager. To disable the auto-approval, because, e.g., you are using approver-policy, you can enable 'disableAutoApproval'. ref: https://cert-manager.io/docs/concepts/certificaterequest/#approval #### **extraArgs** ~ `array` @@ -684,7 +716,7 @@ enableServiceLinks indicates whether information about services should be inject Enable Prometheus monitoring for the cert-manager controller and webhook. If you use the Prometheus Operator, set prometheus.podmonitor.enabled or prometheus.servicemonitor.enabled, to create a PodMonitor or a ServiceMonitor resource. -Otherwise, 'prometheus.io' annotations are added to the cert-manager and cert-manager-webhook Deployments. Note that you can not enable both PodMonitor and ServiceMonitor as they are mutually exclusive. Enabling both will result in an error. +Otherwise, 'prometheus.io' annotations are added to the cert-manager and cert-manager-webhook Deployments. Note that you cannot enable both PodMonitor and ServiceMonitor as they are mutually exclusive. Enabling both will result in an error. #### **prometheus.servicemonitor.enabled** ~ `bool` > Default value: > ```yaml @@ -703,13 +735,14 @@ The namespace that the service monitor should live in, defaults to the cert-mana > ``` Specifies the `prometheus` label on the created ServiceMonitor. This is used when different Prometheus instances have label selectors matching different ServiceMonitors. -#### **prometheus.servicemonitor.targetPort** ~ `number` +#### **prometheus.servicemonitor.targetPort** ~ `string,integer` > Default value: > ```yaml -> 9402 +> http-metrics > ``` The target port to set on the ServiceMonitor. This must match the port that the cert-manager controller is listening on for metrics. + #### **prometheus.servicemonitor.path** ~ `string` > Default value: > ```yaml @@ -969,13 +1002,13 @@ This prevents downtime during voluntary disruptions such as during a Node upgrad Pod is currently running. #### **webhook.podDisruptionBudget.minAvailable** ~ `unknown` -This property configures the minimum available pods for disruptions. Can either be set to an integer (e.g. 1) or a percentage value (e.g. 25%). +This property configures the minimum available pods for disruptions. Can either be set to an integer (e.g., 1) or a percentage value (e.g., 25%). It cannot be used if `maxUnavailable` is set. #### **webhook.podDisruptionBudget.maxUnavailable** ~ `unknown` -This property configures the maximum unavailable pods for disruptions. Can either be set to an integer (e.g. 1) or a percentage value (e.g. 25%). +This property configures the maximum unavailable pods for disruptions. Can either be set to an integer (e.g., 1) or a percentage value (e.g., 25%). It cannot be used if `minAvailable` is set. @@ -1293,6 +1326,8 @@ Create network policies for the webhooks. > - from: > - ipBlock: > cidr: 0.0.0.0/0 +> - ipBlock: +> cidr: ::/0 > ``` Ingress rule for the webhook network policy. By default, it allows all inbound traffic. @@ -1314,6 +1349,8 @@ Ingress rule for the webhook network policy. By default, it allows all inbound t > to: > - ipBlock: > cidr: 0.0.0.0/0 +> - ipBlock: +> cidr: ::/0 > ``` Egress rule for the webhook network policy. By default, it allows all outbound traffic to ports 80 and 443, as well as DNS ports. @@ -1442,14 +1479,14 @@ Pod is currently running. #### **cainjector.podDisruptionBudget.minAvailable** ~ `unknown` `minAvailable` configures the minimum available pods for disruptions. It can either be set to -an integer (e.g. 1) or a percentage value (e.g. 25%). +an integer (e.g., 1) or a percentage value (e.g., 25%). Cannot be used if `maxUnavailable` is set. #### **cainjector.podDisruptionBudget.maxUnavailable** ~ `unknown` `maxUnavailable` configures the maximum unavailable pods for disruptions. It can either be set to -an integer (e.g. 1) or a percentage value (e.g. 25%). +an integer (e.g., 1) or a percentage value (e.g., 25%). Cannot be used if `minAvailable` is set. diff --git a/packages/system/cert-manager/charts/cert-manager/templates/NOTES.txt b/packages/system/cert-manager/charts/cert-manager/templates/NOTES.txt index 341d1012..4d0b4b60 100644 --- a/packages/system/cert-manager/charts/cert-manager/templates/NOTES.txt +++ b/packages/system/cert-manager/charts/cert-manager/templates/NOTES.txt @@ -1,6 +1,12 @@ {{- if .Values.installCRDs }} ⚠️ WARNING: `installCRDs` is deprecated, use `crds.enabled` instead. + {{- end }} +⚠️ WARNING: New default private key rotation policy for Certificate resources. +The default private key rotation policy for Certificate resources was +changed to `Always` in cert-manager >= v1.18.0. +Learn more in the [1.18 release notes](https://cert-manager.io/docs/releases/release-notes/release-notes-1.18). + cert-manager {{ .Chart.AppVersion }} has been deployed successfully! In order to begin issuing certificates, you will need to set up a ClusterIssuer diff --git a/packages/system/cert-manager/charts/cert-manager/templates/_helpers.tpl b/packages/system/cert-manager/charts/cert-manager/templates/_helpers.tpl index e15fa191..f85373f3 100644 --- a/packages/system/cert-manager/charts/cert-manager/templates/_helpers.tpl +++ b/packages/system/cert-manager/charts/cert-manager/templates/_helpers.tpl @@ -187,6 +187,17 @@ See https://github.com/cert-manager/cert-manager/issues/6329 for a list of linke {{- end }} {{- end }} +{{/* +Labels for the CRD resources. +*/}} +{{- define "cert-manager.crd-labels" -}} +app: "{{ template "cert-manager.name" . }}" +app.kubernetes.io/name: "{{ template "cert-manager.name" . }}" +app.kubernetes.io/instance: "{{ .Release.Name }}" +app.kubernetes.io/component: "crds" +{{ include "labels" . }} +{{- end -}} + {{/* Check that the user has not set both .installCRDs and .crds.enabled or set .installCRDs and disabled .crds.keep. diff --git a/packages/system/cert-manager/charts/cert-manager/templates/cainjector-deployment.yaml b/packages/system/cert-manager/charts/cert-manager/templates/cainjector-deployment.yaml index dc14ab02..b5434caa 100644 --- a/packages/system/cert-manager/charts/cert-manager/templates/cainjector-deployment.yaml +++ b/packages/system/cert-manager/charts/cert-manager/templates/cainjector-deployment.yaml @@ -67,6 +67,9 @@ spec: {{- with .Values.global.priorityClassName }} priorityClassName: {{ . | quote }} {{- end }} + {{- if (hasKey .Values.global "hostUsers") }} + hostUsers: {{ .Values.global.hostUsers }} + {{- end }} {{- with .Values.cainjector.securityContext }} securityContext: {{- toYaml . | nindent 8 }} @@ -136,9 +139,13 @@ spec: {{- toYaml . | nindent 12 }} {{- end }} {{- end }} - {{- with .Values.cainjector.nodeSelector }} + {{- $nodeSelector := .Values.global.nodeSelector | default dict }} + {{- $nodeSelector = merge $nodeSelector (.Values.cainjector.nodeSelector | default dict) }} + {{- with $nodeSelector }} nodeSelector: - {{- toYaml . | nindent 8 }} + {{- range $key, $value := . }} + {{ $key }}: {{ $value | quote }} + {{- end }} {{- end }} {{- with .Values.cainjector.affinity }} affinity: diff --git a/packages/system/cert-manager/charts/cert-manager/templates/crds.yaml b/packages/system/cert-manager/charts/cert-manager/templates/crds.yaml deleted file mode 100644 index f5f8ec43..00000000 --- a/packages/system/cert-manager/charts/cert-manager/templates/crds.yaml +++ /dev/null @@ -1,12036 +0,0 @@ -# {{- include "cert-manager.crd-check" . }} -# START crd {{- if or .Values.crds.enabled .Values.installCRDs }} -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: certificaterequests.cert-manager.io - # START annotations {{- if .Values.crds.keep }} - annotations: - helm.sh/resource-policy: keep - # END annotations {{- end }} - labels: - app: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/name: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/instance: '{{ .Release.Name }}' - # Generated labels {{- include "labels" . | nindent 4 }} -spec: - group: cert-manager.io - names: - kind: CertificateRequest - listKind: CertificateRequestList - plural: certificaterequests - shortNames: - - cr - - crs - singular: certificaterequest - categories: - - cert-manager - scope: Namespaced - versions: - - name: v1 - subresources: - status: {} - additionalPrinterColumns: - - jsonPath: .status.conditions[?(@.type=="Approved")].status - name: Approved - type: string - - jsonPath: .status.conditions[?(@.type=="Denied")].status - name: Denied - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .spec.issuerRef.name - name: Issuer - type: string - - jsonPath: .spec.username - name: Requester - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - priority: 1 - type: string - - jsonPath: .metadata.creationTimestamp - description: CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC. - name: Age - type: date - schema: - openAPIV3Schema: - description: |- - A CertificateRequest is used to request a signed certificate from one of the - configured issuers. - - All fields within the CertificateRequest's `spec` are immutable after creation. - A CertificateRequest will either succeed or fail, as denoted by its `Ready` status - condition and its `status.failureTime` field. - - A CertificateRequest is a one-shot resource, meaning it represents a single - point in time request for a certificate and cannot be re-used. - type: object - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: |- - Specification of the desired state of the CertificateRequest resource. - https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status - type: object - required: - - issuerRef - - request - properties: - duration: - description: |- - Requested 'duration' (i.e. lifetime) of the Certificate. Note that the - issuer may choose to ignore the requested duration, just like any other - requested attribute. - type: string - extra: - description: |- - Extra contains extra attributes of the user that created the CertificateRequest. - Populated by the cert-manager webhook on creation and immutable. - type: object - additionalProperties: - type: array - items: - type: string - groups: - description: |- - Groups contains group membership of the user that created the CertificateRequest. - Populated by the cert-manager webhook on creation and immutable. - type: array - items: - type: string - x-kubernetes-list-type: atomic - isCA: - description: |- - Requested basic constraints isCA value. Note that the issuer may choose - to ignore the requested isCA value, just like any other requested attribute. - - NOTE: If the CSR in the `Request` field has a BasicConstraints extension, - it must have the same isCA value as specified here. - - If true, this will automatically add the `cert sign` usage to the list - of requested `usages`. - type: boolean - issuerRef: - description: |- - Reference to the issuer responsible for issuing the certificate. - If the issuer is namespace-scoped, it must be in the same namespace - as the Certificate. If the issuer is cluster-scoped, it can be used - from any namespace. - - The `name` field of the reference must always be specified. - type: object - required: - - name - properties: - group: - description: Group of the resource being referred to. - type: string - kind: - description: Kind of the resource being referred to. - type: string - name: - description: Name of the resource being referred to. - type: string - request: - description: |- - The PEM-encoded X.509 certificate signing request to be submitted to the - issuer for signing. - - If the CSR has a BasicConstraints extension, its isCA attribute must - match the `isCA` value of this CertificateRequest. - If the CSR has a KeyUsage extension, its key usages must match the - key usages in the `usages` field of this CertificateRequest. - If the CSR has a ExtKeyUsage extension, its extended key usages - must match the extended key usages in the `usages` field of this - CertificateRequest. - type: string - format: byte - uid: - description: |- - UID contains the uid of the user that created the CertificateRequest. - Populated by the cert-manager webhook on creation and immutable. - type: string - usages: - description: |- - Requested key usages and extended key usages. - - NOTE: If the CSR in the `Request` field has uses the KeyUsage or - ExtKeyUsage extension, these extensions must have the same values - as specified here without any additional values. - - If unset, defaults to `digital signature` and `key encipherment`. - type: array - items: - description: |- - KeyUsage specifies valid usage contexts for keys. - See: - https://tools.ietf.org/html/rfc5280#section-4.2.1.3 - https://tools.ietf.org/html/rfc5280#section-4.2.1.12 - - Valid KeyUsage values are as follows: - "signing", - "digital signature", - "content commitment", - "key encipherment", - "key agreement", - "data encipherment", - "cert sign", - "crl sign", - "encipher only", - "decipher only", - "any", - "server auth", - "client auth", - "code signing", - "email protection", - "s/mime", - "ipsec end system", - "ipsec tunnel", - "ipsec user", - "timestamping", - "ocsp signing", - "microsoft sgc", - "netscape sgc" - type: string - enum: - - signing - - digital signature - - content commitment - - key encipherment - - key agreement - - data encipherment - - cert sign - - crl sign - - encipher only - - decipher only - - any - - server auth - - client auth - - code signing - - email protection - - s/mime - - ipsec end system - - ipsec tunnel - - ipsec user - - timestamping - - ocsp signing - - microsoft sgc - - netscape sgc - username: - description: |- - Username contains the name of the user that created the CertificateRequest. - Populated by the cert-manager webhook on creation and immutable. - type: string - status: - description: |- - Status of the CertificateRequest. - This is set and managed automatically. - Read-only. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status - type: object - properties: - ca: - description: |- - The PEM encoded X.509 certificate of the signer, also known as the CA - (Certificate Authority). - This is set on a best-effort basis by different issuers. - If not set, the CA is assumed to be unknown/not available. - type: string - format: byte - certificate: - description: |- - The PEM encoded X.509 certificate resulting from the certificate - signing request. - If not set, the CertificateRequest has either not been completed or has - failed. More information on failure can be found by checking the - `conditions` field. - type: string - format: byte - conditions: - description: |- - List of status conditions to indicate the status of a CertificateRequest. - Known condition types are `Ready`, `InvalidRequest`, `Approved` and `Denied`. - type: array - items: - description: CertificateRequestCondition contains condition information for a CertificateRequest. - type: object - required: - - status - - type - properties: - lastTransitionTime: - description: |- - LastTransitionTime is the timestamp corresponding to the last status - change of this condition. - type: string - format: date-time - message: - description: |- - Message is a human readable description of the details of the last - transition, complementing reason. - type: string - reason: - description: |- - Reason is a brief machine readable explanation for the condition's last - transition. - type: string - status: - description: Status of the condition, one of (`True`, `False`, `Unknown`). - type: string - enum: - - "True" - - "False" - - Unknown - type: - description: |- - Type of the condition, known values are (`Ready`, `InvalidRequest`, - `Approved`, `Denied`). - type: string - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - failureTime: - description: |- - FailureTime stores the time that this CertificateRequest failed. This is - used to influence garbage collection and back-off. - type: string - format: date-time - served: true - storage: true - -# END crd {{- end }} - ---- -# START crd {{- if or .Values.crds.enabled .Values.installCRDs }} -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: certificates.cert-manager.io - # START annotations {{- if .Values.crds.keep }} - annotations: - helm.sh/resource-policy: keep - # END annotations {{- end }} - labels: - app: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/name: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/instance: '{{ .Release.Name }}' - # Generated labels {{- include "labels" . | nindent 4 }} -spec: - group: cert-manager.io - names: - kind: Certificate - listKind: CertificateList - plural: certificates - shortNames: - - cert - - certs - singular: certificate - categories: - - cert-manager - scope: Namespaced - versions: - - name: v1 - subresources: - status: {} - additionalPrinterColumns: - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .spec.secretName - name: Secret - type: string - - jsonPath: .spec.issuerRef.name - name: Issuer - priority: 1 - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - priority: 1 - type: string - - jsonPath: .metadata.creationTimestamp - description: CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC. - name: Age - type: date - schema: - openAPIV3Schema: - description: |- - A Certificate resource should be created to ensure an up to date and signed - X.509 certificate is stored in the Kubernetes Secret resource named in `spec.secretName`. - - The stored certificate will be renewed before it expires (as configured by `spec.renewBefore`). - type: object - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: |- - Specification of the desired state of the Certificate resource. - https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status - type: object - required: - - issuerRef - - secretName - properties: - additionalOutputFormats: - description: |- - Defines extra output formats of the private key and signed certificate chain - to be written to this Certificate's target Secret. - - This is a Beta Feature enabled by default. It can be disabled with the - `--feature-gates=AdditionalCertificateOutputFormats=false` option set on both - the controller and webhook components. - type: array - items: - description: |- - CertificateAdditionalOutputFormat defines an additional output format of a - Certificate resource. These contain supplementary data formats of the signed - certificate chain and paired private key. - type: object - required: - - type - properties: - type: - description: |- - Type is the name of the format type that should be written to the - Certificate's target Secret. - type: string - enum: - - DER - - CombinedPEM - commonName: - description: |- - Requested common name X509 certificate subject attribute. - More info: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.6 - NOTE: TLS clients will ignore this value when any subject alternative name is - set (see https://tools.ietf.org/html/rfc6125#section-6.4.4). - - Should have a length of 64 characters or fewer to avoid generating invalid CSRs. - Cannot be set if the `literalSubject` field is set. - type: string - dnsNames: - description: Requested DNS subject alternative names. - type: array - items: - type: string - duration: - description: |- - Requested 'duration' (i.e. lifetime) of the Certificate. Note that the - issuer may choose to ignore the requested duration, just like any other - requested attribute. - - If unset, this defaults to 90 days. - Minimum accepted duration is 1 hour. - Value must be in units accepted by Go time.ParseDuration https://golang.org/pkg/time/#ParseDuration. - type: string - emailAddresses: - description: Requested email subject alternative names. - type: array - items: - type: string - encodeUsagesInRequest: - description: |- - Whether the KeyUsage and ExtKeyUsage extensions should be set in the encoded CSR. - - This option defaults to true, and should only be disabled if the target - issuer does not support CSRs with these X509 KeyUsage/ ExtKeyUsage extensions. - type: boolean - ipAddresses: - description: Requested IP address subject alternative names. - type: array - items: - type: string - isCA: - description: |- - Requested basic constraints isCA value. - The isCA value is used to set the `isCA` field on the created CertificateRequest - resources. Note that the issuer may choose to ignore the requested isCA value, just - like any other requested attribute. - - If true, this will automatically add the `cert sign` usage to the list - of requested `usages`. - type: boolean - issuerRef: - description: |- - Reference to the issuer responsible for issuing the certificate. - If the issuer is namespace-scoped, it must be in the same namespace - as the Certificate. If the issuer is cluster-scoped, it can be used - from any namespace. - - The `name` field of the reference must always be specified. - type: object - required: - - name - properties: - group: - description: Group of the resource being referred to. - type: string - kind: - description: Kind of the resource being referred to. - type: string - name: - description: Name of the resource being referred to. - type: string - keystores: - description: Additional keystore output formats to be stored in the Certificate's Secret. - type: object - properties: - jks: - description: |- - JKS configures options for storing a JKS keystore in the - `spec.secretName` Secret resource. - type: object - required: - - create - properties: - alias: - description: |- - Alias specifies the alias of the key in the keystore, required by the JKS format. - If not provided, the default alias `certificate` will be used. - type: string - create: - description: |- - Create enables JKS keystore creation for the Certificate. - If true, a file named `keystore.jks` will be created in the target - Secret resource, encrypted using the password stored in - `passwordSecretRef` or `password`. - The keystore file will be updated immediately. - If the issuer provided a CA certificate, a file named `truststore.jks` - will also be created in the target Secret resource, encrypted using the - password stored in `passwordSecretRef` - containing the issuing Certificate Authority - type: boolean - password: - description: |- - Password provides a literal password used to encrypt the JKS keystore. - Mutually exclusive with passwordSecretRef. - One of password or passwordSecretRef must provide a password with a non-zero length. - type: string - passwordSecretRef: - description: |- - PasswordSecretRef is a reference to a non-empty key in a Secret resource - containing the password used to encrypt the JKS keystore. - Mutually exclusive with password. - One of password or passwordSecretRef must provide a password with a non-zero length. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - pkcs12: - description: |- - PKCS12 configures options for storing a PKCS12 keystore in the - `spec.secretName` Secret resource. - type: object - required: - - create - properties: - create: - description: |- - Create enables PKCS12 keystore creation for the Certificate. - If true, a file named `keystore.p12` will be created in the target - Secret resource, encrypted using the password stored in - `passwordSecretRef` or in `password`. - The keystore file will be updated immediately. - If the issuer provided a CA certificate, a file named `truststore.p12` will - also be created in the target Secret resource, encrypted using the - password stored in `passwordSecretRef` containing the issuing Certificate - Authority - type: boolean - password: - description: |- - Password provides a literal password used to encrypt the PKCS#12 keystore. - Mutually exclusive with passwordSecretRef. - One of password or passwordSecretRef must provide a password with a non-zero length. - type: string - passwordSecretRef: - description: |- - PasswordSecretRef is a reference to a non-empty key in a Secret resource - containing the password used to encrypt the PKCS#12 keystore. - Mutually exclusive with password. - One of password or passwordSecretRef must provide a password with a non-zero length. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - profile: - description: |- - Profile specifies the key and certificate encryption algorithms and the HMAC algorithm - used to create the PKCS12 keystore. Default value is `LegacyRC2` for backward compatibility. - - If provided, allowed values are: - `LegacyRC2`: Deprecated. Not supported by default in OpenSSL 3 or Java 20. - `LegacyDES`: Less secure algorithm. Use this option for maximal compatibility. - `Modern2023`: Secure algorithm. Use this option in case you have to always use secure algorithms - (eg. because of company policy). Please note that the security of the algorithm is not that important - in reality, because the unencrypted certificate and private key are also stored in the Secret. - type: string - enum: - - LegacyRC2 - - LegacyDES - - Modern2023 - literalSubject: - description: |- - Requested X.509 certificate subject, represented using the LDAP "String - Representation of a Distinguished Name" [1]. - Important: the LDAP string format also specifies the order of the attributes - in the subject, this is important when issuing certs for LDAP authentication. - Example: `CN=foo,DC=corp,DC=example,DC=com` - More info [1]: https://datatracker.ietf.org/doc/html/rfc4514 - More info: https://github.com/cert-manager/cert-manager/issues/3203 - More info: https://github.com/cert-manager/cert-manager/issues/4424 - - Cannot be set if the `subject` or `commonName` field is set. - type: string - nameConstraints: - description: |- - x.509 certificate NameConstraint extension which MUST NOT be used in a non-CA certificate. - More Info: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.10 - - This is an Alpha Feature and is only enabled with the - `--feature-gates=NameConstraints=true` option set on both - the controller and webhook components. - type: object - properties: - critical: - description: if true then the name constraints are marked critical. - type: boolean - excluded: - description: |- - Excluded contains the constraints which must be disallowed. Any name matching a - restriction in the excluded field is invalid regardless - of information appearing in the permitted - type: object - properties: - dnsDomains: - description: DNSDomains is a list of DNS domains that are permitted or excluded. - type: array - items: - type: string - emailAddresses: - description: EmailAddresses is a list of Email Addresses that are permitted or excluded. - type: array - items: - type: string - ipRanges: - description: |- - IPRanges is a list of IP Ranges that are permitted or excluded. - This should be a valid CIDR notation. - type: array - items: - type: string - uriDomains: - description: URIDomains is a list of URI domains that are permitted or excluded. - type: array - items: - type: string - permitted: - description: Permitted contains the constraints in which the names must be located. - type: object - properties: - dnsDomains: - description: DNSDomains is a list of DNS domains that are permitted or excluded. - type: array - items: - type: string - emailAddresses: - description: EmailAddresses is a list of Email Addresses that are permitted or excluded. - type: array - items: - type: string - ipRanges: - description: |- - IPRanges is a list of IP Ranges that are permitted or excluded. - This should be a valid CIDR notation. - type: array - items: - type: string - uriDomains: - description: URIDomains is a list of URI domains that are permitted or excluded. - type: array - items: - type: string - otherNames: - description: |- - `otherNames` is an escape hatch for SAN that allows any type. We currently restrict the support to string like otherNames, cf RFC 5280 p 37 - Any UTF8 String valued otherName can be passed with by setting the keys oid: x.x.x.x and UTF8Value: somevalue for `otherName`. - Most commonly this would be UPN set with oid: 1.3.6.1.4.1.311.20.2.3 - You should ensure that any OID passed is valid for the UTF8String type as we do not explicitly validate this. - type: array - items: - type: object - properties: - oid: - description: |- - OID is the object identifier for the otherName SAN. - The object identifier must be expressed as a dotted string, for - example, "1.2.840.113556.1.4.221". - type: string - utf8Value: - description: |- - utf8Value is the string value of the otherName SAN. - The utf8Value accepts any valid UTF8 string to set as value for the otherName SAN. - type: string - privateKey: - description: |- - Private key options. These include the key algorithm and size, the used - encoding and the rotation policy. - type: object - properties: - algorithm: - description: |- - Algorithm is the private key algorithm of the corresponding private key - for this certificate. - - If provided, allowed values are either `RSA`, `ECDSA` or `Ed25519`. - If `algorithm` is specified and `size` is not provided, - key size of 2048 will be used for `RSA` key algorithm and - key size of 256 will be used for `ECDSA` key algorithm. - key size is ignored when using the `Ed25519` key algorithm. - type: string - enum: - - RSA - - ECDSA - - Ed25519 - encoding: - description: |- - The private key cryptography standards (PKCS) encoding for this - certificate's private key to be encoded in. - - If provided, allowed values are `PKCS1` and `PKCS8` standing for PKCS#1 - and PKCS#8, respectively. - Defaults to `PKCS1` if not specified. - type: string - enum: - - PKCS1 - - PKCS8 - rotationPolicy: - description: |- - RotationPolicy controls how private keys should be regenerated when a - re-issuance is being processed. - - If set to `Never`, a private key will only be generated if one does not - already exist in the target `spec.secretName`. If one does exist but it - does not have the correct algorithm or size, a warning will be raised - to await user intervention. - If set to `Always`, a private key matching the specified requirements - will be generated whenever a re-issuance occurs. - Default is `Never` for backward compatibility. - type: string - enum: - - Never - - Always - size: - description: |- - Size is the key bit size of the corresponding private key for this certificate. - - If `algorithm` is set to `RSA`, valid values are `2048`, `4096` or `8192`, - and will default to `2048` if not specified. - If `algorithm` is set to `ECDSA`, valid values are `256`, `384` or `521`, - and will default to `256` if not specified. - If `algorithm` is set to `Ed25519`, Size is ignored. - No other values are allowed. - type: integer - renewBefore: - description: |- - How long before the currently issued certificate's expiry cert-manager should - renew the certificate. For example, if a certificate is valid for 60 minutes, - and `renewBefore=10m`, cert-manager will begin to attempt to renew the certificate - 50 minutes after it was issued (i.e. when there are 10 minutes remaining until - the certificate is no longer valid). - - NOTE: The actual lifetime of the issued certificate is used to determine the - renewal time. If an issuer returns a certificate with a different lifetime than - the one requested, cert-manager will use the lifetime of the issued certificate. - - If unset, this defaults to 1/3 of the issued certificate's lifetime. - Minimum accepted value is 5 minutes. - Value must be in units accepted by Go time.ParseDuration https://golang.org/pkg/time/#ParseDuration. - Cannot be set if the `renewBeforePercentage` field is set. - type: string - renewBeforePercentage: - description: |- - `renewBeforePercentage` is like `renewBefore`, except it is a relative percentage - rather than an absolute duration. For example, if a certificate is valid for 60 - minutes, and `renewBeforePercentage=25`, cert-manager will begin to attempt to - renew the certificate 45 minutes after it was issued (i.e. when there are 15 - minutes (25%) remaining until the certificate is no longer valid). - - NOTE: The actual lifetime of the issued certificate is used to determine the - renewal time. If an issuer returns a certificate with a different lifetime than - the one requested, cert-manager will use the lifetime of the issued certificate. - - Value must be an integer in the range (0,100). The minimum effective - `renewBefore` derived from the `renewBeforePercentage` and `duration` fields is 5 - minutes. - Cannot be set if the `renewBefore` field is set. - type: integer - format: int32 - revisionHistoryLimit: - description: |- - The maximum number of CertificateRequest revisions that are maintained in - the Certificate's history. Each revision represents a single `CertificateRequest` - created by this Certificate, either when it was created, renewed, or Spec - was changed. Revisions will be removed by oldest first if the number of - revisions exceeds this number. - - If set, revisionHistoryLimit must be a value of `1` or greater. - If unset (`nil`), revisions will not be garbage collected. - Default value is `nil`. - type: integer - format: int32 - secretName: - description: |- - Name of the Secret resource that will be automatically created and - managed by this Certificate resource. It will be populated with a - private key and certificate, signed by the denoted issuer. The Secret - resource lives in the same namespace as the Certificate resource. - type: string - secretTemplate: - description: |- - Defines annotations and labels to be copied to the Certificate's Secret. - Labels and annotations on the Secret will be changed as they appear on the - SecretTemplate when added or removed. SecretTemplate annotations are added - in conjunction with, and cannot overwrite, the base set of annotations - cert-manager sets on the Certificate's Secret. - type: object - properties: - annotations: - description: Annotations is a key value map to be copied to the target Kubernetes Secret. - type: object - additionalProperties: - type: string - labels: - description: Labels is a key value map to be copied to the target Kubernetes Secret. - type: object - additionalProperties: - type: string - subject: - description: |- - Requested set of X509 certificate subject attributes. - More info: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.6 - - The common name attribute is specified separately in the `commonName` field. - Cannot be set if the `literalSubject` field is set. - type: object - properties: - countries: - description: Countries to be used on the Certificate. - type: array - items: - type: string - localities: - description: Cities to be used on the Certificate. - type: array - items: - type: string - organizationalUnits: - description: Organizational Units to be used on the Certificate. - type: array - items: - type: string - organizations: - description: Organizations to be used on the Certificate. - type: array - items: - type: string - postalCodes: - description: Postal codes to be used on the Certificate. - type: array - items: - type: string - provinces: - description: State/Provinces to be used on the Certificate. - type: array - items: - type: string - serialNumber: - description: Serial number to be used on the Certificate. - type: string - streetAddresses: - description: Street addresses to be used on the Certificate. - type: array - items: - type: string - uris: - description: Requested URI subject alternative names. - type: array - items: - type: string - usages: - description: |- - Requested key usages and extended key usages. - These usages are used to set the `usages` field on the created CertificateRequest - resources. If `encodeUsagesInRequest` is unset or set to `true`, the usages - will additionally be encoded in the `request` field which contains the CSR blob. - - If unset, defaults to `digital signature` and `key encipherment`. - type: array - items: - description: |- - KeyUsage specifies valid usage contexts for keys. - See: - https://tools.ietf.org/html/rfc5280#section-4.2.1.3 - https://tools.ietf.org/html/rfc5280#section-4.2.1.12 - - Valid KeyUsage values are as follows: - "signing", - "digital signature", - "content commitment", - "key encipherment", - "key agreement", - "data encipherment", - "cert sign", - "crl sign", - "encipher only", - "decipher only", - "any", - "server auth", - "client auth", - "code signing", - "email protection", - "s/mime", - "ipsec end system", - "ipsec tunnel", - "ipsec user", - "timestamping", - "ocsp signing", - "microsoft sgc", - "netscape sgc" - type: string - enum: - - signing - - digital signature - - content commitment - - key encipherment - - key agreement - - data encipherment - - cert sign - - crl sign - - encipher only - - decipher only - - any - - server auth - - client auth - - code signing - - email protection - - s/mime - - ipsec end system - - ipsec tunnel - - ipsec user - - timestamping - - ocsp signing - - microsoft sgc - - netscape sgc - status: - description: |- - Status of the Certificate. - This is set and managed automatically. - Read-only. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status - type: object - properties: - conditions: - description: |- - List of status conditions to indicate the status of certificates. - Known condition types are `Ready` and `Issuing`. - type: array - items: - description: CertificateCondition contains condition information for a Certificate. - type: object - required: - - status - - type - properties: - lastTransitionTime: - description: |- - LastTransitionTime is the timestamp corresponding to the last status - change of this condition. - type: string - format: date-time - message: - description: |- - Message is a human readable description of the details of the last - transition, complementing reason. - type: string - observedGeneration: - description: |- - If set, this represents the .metadata.generation that the condition was - set based upon. - For instance, if .metadata.generation is currently 12, but the - .status.condition[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the Certificate. - type: integer - format: int64 - reason: - description: |- - Reason is a brief machine readable explanation for the condition's last - transition. - type: string - status: - description: Status of the condition, one of (`True`, `False`, `Unknown`). - type: string - enum: - - "True" - - "False" - - Unknown - type: - description: Type of the condition, known values are (`Ready`, `Issuing`). - type: string - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - failedIssuanceAttempts: - description: |- - The number of continuous failed issuance attempts up till now. This - field gets removed (if set) on a successful issuance and gets set to - 1 if unset and an issuance has failed. If an issuance has failed, the - delay till the next issuance will be calculated using formula - time.Hour * 2 ^ (failedIssuanceAttempts - 1). - type: integer - lastFailureTime: - description: |- - LastFailureTime is set only if the latest issuance for this - Certificate failed and contains the time of the failure. If an - issuance has failed, the delay till the next issuance will be - calculated using formula time.Hour * 2 ^ (failedIssuanceAttempts - - 1). If the latest issuance has succeeded this field will be unset. - type: string - format: date-time - nextPrivateKeySecretName: - description: |- - The name of the Secret resource containing the private key to be used - for the next certificate iteration. - The keymanager controller will automatically set this field if the - `Issuing` condition is set to `True`. - It will automatically unset this field when the Issuing condition is - not set or False. - type: string - notAfter: - description: |- - The expiration time of the certificate stored in the secret named - by this resource in `spec.secretName`. - type: string - format: date-time - notBefore: - description: |- - The time after which the certificate stored in the secret named - by this resource in `spec.secretName` is valid. - type: string - format: date-time - renewalTime: - description: |- - RenewalTime is the time at which the certificate will be next - renewed. - If not set, no upcoming renewal is scheduled. - type: string - format: date-time - revision: - description: |- - The current 'revision' of the certificate as issued. - - When a CertificateRequest resource is created, it will have the - `cert-manager.io/certificate-revision` set to one greater than the - current value of this field. - - Upon issuance, this field will be set to the value of the annotation - on the CertificateRequest resource used to issue the certificate. - - Persisting the value on the CertificateRequest resource allows the - certificates controller to know whether a request is part of an old - issuance or if it is part of the ongoing revision's issuance by - checking if the revision value in the annotation is greater than this - field. - type: integer - served: true - storage: true - -# END crd {{- end }} - ---- -# START crd {{- if or .Values.crds.enabled .Values.installCRDs }} -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: challenges.acme.cert-manager.io - # START annotations {{- if .Values.crds.keep }} - annotations: - helm.sh/resource-policy: keep - # END annotations {{- end }} - labels: - app: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/name: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/instance: '{{ .Release.Name }}' - # Generated labels {{- include "labels" . | nindent 4 }} -spec: - group: acme.cert-manager.io - names: - kind: Challenge - listKind: ChallengeList - plural: challenges - singular: challenge - categories: - - cert-manager - - cert-manager-acme - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.state - name: State - type: string - - jsonPath: .spec.dnsName - name: Domain - type: string - - jsonPath: .status.reason - name: Reason - priority: 1 - type: string - - description: CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC. - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1 - schema: - openAPIV3Schema: - description: Challenge is a type to represent a Challenge request with an ACME server - type: object - required: - - metadata - - spec - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - type: object - required: - - authorizationURL - - dnsName - - issuerRef - - key - - solver - - token - - type - - url - properties: - authorizationURL: - description: |- - The URL to the ACME Authorization resource that this - challenge is a part of. - type: string - dnsName: - description: |- - dnsName is the identifier that this challenge is for, e.g. example.com. - If the requested DNSName is a 'wildcard', this field MUST be set to the - non-wildcard domain, e.g. for `*.example.com`, it must be `example.com`. - type: string - issuerRef: - description: |- - References a properly configured ACME-type Issuer which should - be used to create this Challenge. - If the Issuer does not exist, processing will be retried. - If the Issuer is not an 'ACME' Issuer, an error will be returned and the - Challenge will be marked as failed. - type: object - required: - - name - properties: - group: - description: Group of the resource being referred to. - type: string - kind: - description: Kind of the resource being referred to. - type: string - name: - description: Name of the resource being referred to. - type: string - key: - description: |- - The ACME challenge key for this challenge - For HTTP01 challenges, this is the value that must be responded with to - complete the HTTP01 challenge in the format: - `.`. - For DNS01 challenges, this is the base64 encoded SHA256 sum of the - `.` - text that must be set as the TXT record content. - type: string - solver: - description: |- - Contains the domain solving configuration that should be used to - solve this challenge resource. - type: object - properties: - dns01: - description: |- - Configures cert-manager to attempt to complete authorizations by - performing the DNS01 challenge flow. - type: object - properties: - acmeDNS: - description: |- - Use the 'ACME DNS' (https://github.com/joohoi/acme-dns) API to manage - DNS01 challenge records. - type: object - required: - - accountSecretRef - - host - properties: - accountSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - host: - type: string - akamai: - description: Use the Akamai DNS zone management API to manage DNS01 challenge records. - type: object - required: - - accessTokenSecretRef - - clientSecretSecretRef - - clientTokenSecretRef - - serviceConsumerDomain - properties: - accessTokenSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientSecretSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientTokenSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - serviceConsumerDomain: - type: string - azureDNS: - description: Use the Microsoft Azure DNS API to manage DNS01 challenge records. - type: object - required: - - resourceGroupName - - subscriptionID - properties: - clientID: - description: |- - Auth: Azure Service Principal: - The ClientID of the Azure Service Principal used to authenticate with Azure DNS. - If set, ClientSecret and TenantID must also be set. - type: string - clientSecretSecretRef: - description: |- - Auth: Azure Service Principal: - A reference to a Secret containing the password associated with the Service Principal. - If set, ClientID and TenantID must also be set. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - environment: - description: name of the Azure environment (default AzurePublicCloud) - type: string - enum: - - AzurePublicCloud - - AzureChinaCloud - - AzureGermanCloud - - AzureUSGovernmentCloud - hostedZoneName: - description: name of the DNS zone that should be used - type: string - managedIdentity: - description: |- - Auth: Azure Workload Identity or Azure Managed Service Identity: - Settings to enable Azure Workload Identity or Azure Managed Service Identity - If set, ClientID, ClientSecret and TenantID must not be set. - type: object - properties: - clientID: - description: client ID of the managed identity, can not be used at the same time as resourceID - type: string - resourceID: - description: |- - resource ID of the managed identity, can not be used at the same time as clientID - Cannot be used for Azure Managed Service Identity - type: string - tenantID: - description: tenant ID of the managed identity, can not be used at the same time as resourceID - type: string - resourceGroupName: - description: resource group the DNS zone is located in - type: string - subscriptionID: - description: ID of the Azure subscription - type: string - tenantID: - description: |- - Auth: Azure Service Principal: - The TenantID of the Azure Service Principal used to authenticate with Azure DNS. - If set, ClientID and ClientSecret must also be set. - type: string - cloudDNS: - description: Use the Google Cloud DNS API to manage DNS01 challenge records. - type: object - required: - - project - properties: - hostedZoneName: - description: |- - HostedZoneName is an optional field that tells cert-manager in which - Cloud DNS zone the challenge record has to be created. - If left empty cert-manager will automatically choose a zone. - type: string - project: - type: string - serviceAccountSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - cloudflare: - description: Use the Cloudflare API to manage DNS01 challenge records. - type: object - properties: - apiKeySecretRef: - description: |- - API key to use to authenticate with Cloudflare. - Note: using an API token to authenticate is now the recommended method - as it allows greater control of permissions. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - apiTokenSecretRef: - description: API token used to authenticate with Cloudflare. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - email: - description: Email of the account, only required when using API key based authentication. - type: string - cnameStrategy: - description: |- - CNAMEStrategy configures how the DNS01 provider should handle CNAME - records when found in DNS zones. - type: string - enum: - - None - - Follow - digitalocean: - description: Use the DigitalOcean DNS API to manage DNS01 challenge records. - type: object - required: - - tokenSecretRef - properties: - tokenSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - rfc2136: - description: |- - Use RFC2136 ("Dynamic Updates in the Domain Name System") (https://datatracker.ietf.org/doc/rfc2136/) - to manage DNS01 challenge records. - type: object - required: - - nameserver - properties: - nameserver: - description: |- - The IP address or hostname of an authoritative DNS server supporting - RFC2136 in the form host:port. If the host is an IPv6 address it must be - enclosed in square brackets (e.g [2001:db8::1]) ; port is optional. - This field is required. - type: string - tsigAlgorithm: - description: |- - The TSIG Algorithm configured in the DNS supporting RFC2136. Used only - when ``tsigSecretSecretRef`` and ``tsigKeyName`` are defined. - Supported values are (case-insensitive): ``HMACMD5`` (default), - ``HMACSHA1``, ``HMACSHA256`` or ``HMACSHA512``. - type: string - tsigKeyName: - description: |- - The TSIG Key name configured in the DNS. - If ``tsigSecretSecretRef`` is defined, this field is required. - type: string - tsigSecretSecretRef: - description: |- - The name of the secret containing the TSIG value. - If ``tsigKeyName`` is defined, this field is required. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - route53: - description: Use the AWS Route53 API to manage DNS01 challenge records. - type: object - properties: - accessKeyID: - description: |- - The AccessKeyID is used for authentication. - Cannot be set when SecretAccessKeyID is set. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, - see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials - type: string - accessKeyIDSecretRef: - description: |- - The SecretAccessKey is used for authentication. If set, pull the AWS - access key ID from a key within a Kubernetes Secret. - Cannot be set when AccessKeyID is set. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, - see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - auth: - description: Auth configures how cert-manager authenticates. - type: object - required: - - kubernetes - properties: - kubernetes: - description: |- - Kubernetes authenticates with Route53 using AssumeRoleWithWebIdentity - by passing a bound ServiceAccount token. - type: object - required: - - serviceAccountRef - properties: - serviceAccountRef: - description: |- - A reference to a service account that will be used to request a bound - token (also known as "projected token"). To use this field, you must - configure an RBAC rule to let cert-manager request a token. - type: object - required: - - name - properties: - audiences: - description: |- - TokenAudiences is an optional list of audiences to include in the - token passed to AWS. The default token consisting of the issuer's namespace - and name is always included. - If unset the audience defaults to `sts.amazonaws.com`. - type: array - items: - type: string - name: - description: Name of the ServiceAccount used to request a token. - type: string - hostedZoneID: - description: If set, the provider will manage only this zone in Route53 and will not do a lookup using the route53:ListHostedZonesByName api call. - type: string - region: - description: |- - Override the AWS region. - - Route53 is a global service and does not have regional endpoints but the - region specified here (or via environment variables) is used as a hint to - help compute the correct AWS credential scope and partition when it - connects to Route53. See: - - [Amazon Route 53 endpoints and quotas](https://docs.aws.amazon.com/general/latest/gr/r53.html) - - [Global services](https://docs.aws.amazon.com/whitepapers/latest/aws-fault-isolation-boundaries/global-services.html) - - If you omit this region field, cert-manager will use the region from - AWS_REGION and AWS_DEFAULT_REGION environment variables, if they are set - in the cert-manager controller Pod. - - The `region` field is not needed if you use [IAM Roles for Service Accounts (IRSA)](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html). - Instead an AWS_REGION environment variable is added to the cert-manager controller Pod by: - [Amazon EKS Pod Identity Webhook](https://github.com/aws/amazon-eks-pod-identity-webhook). - In this case this `region` field value is ignored. - - The `region` field is not needed if you use [EKS Pod Identities](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html). - Instead an AWS_REGION environment variable is added to the cert-manager controller Pod by: - [Amazon EKS Pod Identity Agent](https://github.com/aws/eks-pod-identity-agent), - In this case this `region` field value is ignored. - type: string - role: - description: |- - Role is a Role ARN which the Route53 provider will assume using either the explicit credentials AccessKeyID/SecretAccessKey - or the inferred credentials from environment variables, shared credentials file or AWS Instance metadata - type: string - secretAccessKeySecretRef: - description: |- - The SecretAccessKey is used for authentication. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, - see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - webhook: - description: |- - Configure an external webhook based DNS01 challenge solver to manage - DNS01 challenge records. - type: object - required: - - groupName - - solverName - properties: - config: - description: |- - Additional configuration that should be passed to the webhook apiserver - when challenges are processed. - This can contain arbitrary JSON data. - Secret values should not be specified in this stanza. - If secret values are needed (e.g. credentials for a DNS service), you - should use a SecretKeySelector to reference a Secret resource. - For details on the schema of this field, consult the webhook provider - implementation's documentation. - x-kubernetes-preserve-unknown-fields: true - groupName: - description: |- - The API group name that should be used when POSTing ChallengePayload - resources to the webhook apiserver. - This should be the same as the GroupName specified in the webhook - provider implementation. - type: string - solverName: - description: |- - The name of the solver to use, as defined in the webhook provider - implementation. - This will typically be the name of the provider, e.g. 'cloudflare'. - type: string - http01: - description: |- - Configures cert-manager to attempt to complete authorizations by - performing the HTTP01 challenge flow. - It is not possible to obtain certificates for wildcard domain names - (e.g. `*.example.com`) using the HTTP01 challenge mechanism. - type: object - properties: - gatewayHTTPRoute: - description: |- - The Gateway API is a sig-network community API that models service networking - in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will - create HTTPRoutes with the specified labels in the same namespace as the challenge. - This solver is experimental, and fields / behaviour may change in the future. - type: object - properties: - labels: - description: |- - Custom labels that will be applied to HTTPRoutes created by cert-manager - while solving HTTP-01 challenges. - type: object - additionalProperties: - type: string - parentRefs: - description: |- - When solving an HTTP-01 challenge, cert-manager creates an HTTPRoute. - cert-manager needs to know which parentRefs should be used when creating - the HTTPRoute. Usually, the parentRef references a Gateway. See: - https://gateway-api.sigs.k8s.io/api-types/httproute/#attaching-to-gateways - type: array - items: - description: |- - ParentReference identifies an API object (usually a Gateway) that can be considered - a parent of this resource (usually a route). There are two kinds of parent resources - with "Core" support: - - * Gateway (Gateway conformance profile) - * Service (Mesh conformance profile, ClusterIP Services only) - - This API may be extended in the future to support additional kinds of parent - resources. - - The API object must be valid in the cluster; the Group and Kind must - be registered in the cluster for this reference to be valid. - type: object - required: - - name - properties: - group: - description: |- - Group is the group of the referent. - When unspecified, "gateway.networking.k8s.io" is inferred. - To set the core API group (such as for a "Service" kind referent), - Group must be explicitly set to "" (empty string). - - Support: Core - type: string - default: gateway.networking.k8s.io - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - kind: - description: |- - Kind is kind of the referent. - - There are two kinds of parent resources with "Core" support: - - * Gateway (Gateway conformance profile) - * Service (Mesh conformance profile, ClusterIP Services only) - - Support for other resources is Implementation-Specific. - type: string - default: Gateway - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - name: - description: |- - Name is the name of the referent. - - Support: Core - type: string - maxLength: 253 - minLength: 1 - namespace: - description: |- - Namespace is the namespace of the referent. When unspecified, this refers - to the local namespace of the Route. - - Note that there are specific rules for ParentRefs which cross namespace - boundaries. Cross-namespace references are only valid if they are explicitly - allowed by something in the namespace they are referring to. For example: - Gateway has the AllowedRoutes field, and ReferenceGrant provides a - generic way to enable any other kind of cross-namespace reference. - - - ParentRefs from a Route to a Service in the same namespace are "producer" - routes, which apply default routing rules to inbound connections from - any namespace to the Service. - - ParentRefs from a Route to a Service in a different namespace are - "consumer" routes, and these routing rules are only applied to outbound - connections originating from the same namespace as the Route, for which - the intended destination of the connections are a Service targeted as a - ParentRef of the Route. - - - Support: Core - type: string - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - port: - description: |- - Port is the network port this Route targets. It can be interpreted - differently based on the type of parent resource. - - When the parent resource is a Gateway, this targets all listeners - listening on the specified port that also support this kind of Route(and - select this Route). It's not recommended to set `Port` unless the - networking behaviors specified in a Route must apply to a specific port - as opposed to a listener(s) whose port(s) may be changed. When both Port - and SectionName are specified, the name and port of the selected listener - must match both specified values. - - - When the parent resource is a Service, this targets a specific port in the - Service spec. When both Port (experimental) and SectionName are specified, - the name and port of the selected port must match both specified values. - - - Implementations MAY choose to support other parent resources. - Implementations supporting other types of parent resources MUST clearly - document how/if Port is interpreted. - - For the purpose of status, an attachment is considered successful as - long as the parent resource accepts it partially. For example, Gateway - listeners can restrict which Routes can attach to them by Route kind, - namespace, or hostname. If 1 of 2 Gateway listeners accept attachment - from the referencing Route, the Route MUST be considered successfully - attached. If no Gateway listeners accept attachment from this Route, - the Route MUST be considered detached from the Gateway. - - Support: Extended - type: integer - format: int32 - maximum: 65535 - minimum: 1 - sectionName: - description: |- - SectionName is the name of a section within the target resource. In the - following resources, SectionName is interpreted as the following: - - * Gateway: Listener name. When both Port (experimental) and SectionName - are specified, the name and port of the selected listener must match - both specified values. - * Service: Port name. When both Port (experimental) and SectionName - are specified, the name and port of the selected listener must match - both specified values. - - Implementations MAY choose to support attaching Routes to other resources. - If that is the case, they MUST clearly document how SectionName is - interpreted. - - When unspecified (empty string), this will reference the entire resource. - For the purpose of status, an attachment is considered successful if at - least one section in the parent resource accepts it. For example, Gateway - listeners can restrict which Routes can attach to them by Route kind, - namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from - the referencing Route, the Route MUST be considered successfully - attached. If no Gateway listeners accept attachment from this Route, the - Route MUST be considered detached from the Gateway. - - Support: Core - type: string - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - podTemplate: - description: |- - Optional pod template used to configure the ACME challenge solver pods - used for HTTP01 challenges. - type: object - properties: - metadata: - description: |- - ObjectMeta overrides for the pod used to solve HTTP01 challenges. - Only the 'labels' and 'annotations' fields may be set. - If labels or annotations overlap with in-built values, the values here - will override the in-built values. - type: object - properties: - annotations: - description: Annotations that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - labels: - description: Labels that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - spec: - description: |- - PodSpec defines overrides for the HTTP01 challenge solver pod. - Check ACMEChallengeSolverHTTP01IngressPodSpec to find out currently supported fields. - All other fields will be ignored. - type: object - properties: - affinity: - description: If specified, the pod's scheduling constraints - type: object - properties: - nodeAffinity: - description: Describes node affinity scheduling rules for the pod. - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node matches the corresponding matchExpressions; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: |- - An empty preferred scheduling term matches all objects with implicit weight 0 - (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). - type: object - required: - - preference - - weight - properties: - preference: - description: A node selector term, associated with the corresponding weight. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - weight: - description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to an update), the system - may or may not try to eventually evict the pod from its node. - type: object - required: - - nodeSelectorTerms - properties: - nodeSelectorTerms: - description: Required. A list of node selector terms. The terms are ORed. - type: array - items: - description: |- - A null or empty node selector term matches no objects. The requirements of - them are ANDed. - The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - podAffinity: - description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - podAntiAffinity: - description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the anti-affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the anti-affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the anti-affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - imagePullSecrets: - description: If specified, the pod's imagePullSecrets - type: array - items: - description: |- - LocalObjectReference contains enough information to let you locate the - referenced object inside the same namespace. - type: object - properties: - name: - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - default: "" - x-kubernetes-map-type: atomic - nodeSelector: - description: |- - NodeSelector is a selector which must be true for the pod to fit on a node. - Selector which must match a node's labels for the pod to be scheduled on that node. - More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ - type: object - additionalProperties: - type: string - priorityClassName: - description: If specified, the pod's priorityClassName. - type: string - securityContext: - description: If specified, the pod's security context - type: object - properties: - fsGroup: - description: |- - A special supplemental group that applies to all containers in a pod. - Some volume types allow the Kubelet to change the ownership of that volume - to be owned by the pod: - - 1. The owning GID will be the FSGroup - 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) - 3. The permission bits are OR'd with rw-rw---- - - If unset, the Kubelet will not modify the ownership and permissions of any volume. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - fsGroupChangePolicy: - description: |- - fsGroupChangePolicy defines behavior of changing ownership and permission of the volume - before being exposed inside Pod. This field will only apply to - volume types which support fsGroup based ownership(and permissions). - It will have no effect on ephemeral volume types such as: secret, configmaps - and emptydir. - Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. - Note that this field cannot be set when spec.os.name is windows. - type: string - runAsGroup: - description: |- - The GID to run the entrypoint of the container process. - Uses runtime default if unset. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - runAsNonRoot: - description: |- - Indicates that the container must run as a non-root user. - If true, the Kubelet will validate the image at runtime to ensure that it - does not run as UID 0 (root) and fail to start the container if it does. - If unset or false, no such validation will be performed. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: |- - The UID to run the entrypoint of the container process. - Defaults to user specified in image metadata if unspecified. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - seLinuxOptions: - description: |- - The SELinux context to be applied to all containers. - If unspecified, the container runtime will allocate a random SELinux context for each - container. May also be set in SecurityContext. If set in - both SecurityContext and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. - Note that this field cannot be set when spec.os.name is windows. - type: object - properties: - level: - description: Level is SELinux level label that applies to the container. - type: string - role: - description: Role is a SELinux role label that applies to the container. - type: string - type: - description: Type is a SELinux type label that applies to the container. - type: string - user: - description: User is a SELinux user label that applies to the container. - type: string - seccompProfile: - description: |- - The seccomp options to use by the containers in this pod. - Note that this field cannot be set when spec.os.name is windows. - type: object - required: - - type - properties: - localhostProfile: - description: |- - localhostProfile indicates a profile defined in a file on the node should be used. - The profile must be preconfigured on the node to work. - Must be a descending path, relative to the kubelet's configured seccomp profile location. - Must be set if type is "Localhost". Must NOT be set for any other type. - type: string - type: - description: |- - type indicates which kind of seccomp profile will be applied. - Valid options are: - - Localhost - a profile defined in a file on the node should be used. - RuntimeDefault - the container runtime default profile should be used. - Unconfined - no profile should be applied. - type: string - supplementalGroups: - description: |- - A list of groups applied to the first process run in each container, in addition - to the container's primary GID, the fsGroup (if specified), and group memberships - defined in the container image for the uid of the container process. If unspecified, - no additional groups are added to any container. Note that group memberships - defined in the container image for the uid of the container process are still effective, - even if they are not included in this list. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - type: integer - format: int64 - sysctls: - description: |- - Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported - sysctls (by the container runtime) might fail to launch. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - description: Sysctl defines a kernel parameter to be set - type: object - required: - - name - - value - properties: - name: - description: Name of a property to set - type: string - value: - description: Value of a property to set - type: string - serviceAccountName: - description: If specified, the pod's service account - type: string - tolerations: - description: If specified, the pod's tolerations. - type: array - items: - description: |- - The pod this Toleration is attached to tolerates any taint that matches - the triple using the matching operator . - type: object - properties: - effect: - description: |- - Effect indicates the taint effect to match. Empty means match all taint effects. - When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: |- - Key is the taint key that the toleration applies to. Empty means match all taint keys. - If the key is empty, operator must be Exists; this combination means to match all values and all keys. - type: string - operator: - description: |- - Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. - Exists is equivalent to wildcard for value, so that a pod can - tolerate all taints of a particular category. - type: string - tolerationSeconds: - description: |- - TolerationSeconds represents the period of time the toleration (which must be - of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, - it is not set, which means tolerate the taint forever (do not evict). Zero and - negative values will be treated as 0 (evict immediately) by the system. - type: integer - format: int64 - value: - description: |- - Value is the taint value the toleration matches to. - If the operator is Exists, the value should be empty, otherwise just a regular string. - type: string - serviceType: - description: |- - Optional service type for Kubernetes solver service. Supported values - are NodePort or ClusterIP. If unset, defaults to NodePort. - type: string - ingress: - description: |- - The ingress based HTTP01 challenge solver will solve challenges by - creating or modifying Ingress resources in order to route requests for - '/.well-known/acme-challenge/XYZ' to 'challenge solver' pods that are - provisioned by cert-manager for each Challenge to be completed. - type: object - properties: - class: - description: |- - This field configures the annotation `kubernetes.io/ingress.class` when - creating Ingress resources to solve ACME challenges that use this - challenge solver. Only one of `class`, `name` or `ingressClassName` may - be specified. - type: string - ingressClassName: - description: |- - This field configures the field `ingressClassName` on the created Ingress - resources used to solve ACME challenges that use this challenge solver. - This is the recommended way of configuring the ingress class. Only one of - `class`, `name` or `ingressClassName` may be specified. - type: string - ingressTemplate: - description: |- - Optional ingress template used to configure the ACME challenge solver - ingress used for HTTP01 challenges. - type: object - properties: - metadata: - description: |- - ObjectMeta overrides for the ingress used to solve HTTP01 challenges. - Only the 'labels' and 'annotations' fields may be set. - If labels or annotations overlap with in-built values, the values here - will override the in-built values. - type: object - properties: - annotations: - description: Annotations that should be added to the created ACME HTTP01 solver ingress. - type: object - additionalProperties: - type: string - labels: - description: Labels that should be added to the created ACME HTTP01 solver ingress. - type: object - additionalProperties: - type: string - name: - description: |- - The name of the ingress resource that should have ACME challenge solving - routes inserted into it in order to solve HTTP01 challenges. - This is typically used in conjunction with ingress controllers like - ingress-gce, which maintains a 1:1 mapping between external IPs and - ingress resources. Only one of `class`, `name` or `ingressClassName` may - be specified. - type: string - podTemplate: - description: |- - Optional pod template used to configure the ACME challenge solver pods - used for HTTP01 challenges. - type: object - properties: - metadata: - description: |- - ObjectMeta overrides for the pod used to solve HTTP01 challenges. - Only the 'labels' and 'annotations' fields may be set. - If labels or annotations overlap with in-built values, the values here - will override the in-built values. - type: object - properties: - annotations: - description: Annotations that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - labels: - description: Labels that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - spec: - description: |- - PodSpec defines overrides for the HTTP01 challenge solver pod. - Check ACMEChallengeSolverHTTP01IngressPodSpec to find out currently supported fields. - All other fields will be ignored. - type: object - properties: - affinity: - description: If specified, the pod's scheduling constraints - type: object - properties: - nodeAffinity: - description: Describes node affinity scheduling rules for the pod. - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node matches the corresponding matchExpressions; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: |- - An empty preferred scheduling term matches all objects with implicit weight 0 - (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). - type: object - required: - - preference - - weight - properties: - preference: - description: A node selector term, associated with the corresponding weight. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - weight: - description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to an update), the system - may or may not try to eventually evict the pod from its node. - type: object - required: - - nodeSelectorTerms - properties: - nodeSelectorTerms: - description: Required. A list of node selector terms. The terms are ORed. - type: array - items: - description: |- - A null or empty node selector term matches no objects. The requirements of - them are ANDed. - The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - podAffinity: - description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - podAntiAffinity: - description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the anti-affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the anti-affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the anti-affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - imagePullSecrets: - description: If specified, the pod's imagePullSecrets - type: array - items: - description: |- - LocalObjectReference contains enough information to let you locate the - referenced object inside the same namespace. - type: object - properties: - name: - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - default: "" - x-kubernetes-map-type: atomic - nodeSelector: - description: |- - NodeSelector is a selector which must be true for the pod to fit on a node. - Selector which must match a node's labels for the pod to be scheduled on that node. - More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ - type: object - additionalProperties: - type: string - priorityClassName: - description: If specified, the pod's priorityClassName. - type: string - securityContext: - description: If specified, the pod's security context - type: object - properties: - fsGroup: - description: |- - A special supplemental group that applies to all containers in a pod. - Some volume types allow the Kubelet to change the ownership of that volume - to be owned by the pod: - - 1. The owning GID will be the FSGroup - 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) - 3. The permission bits are OR'd with rw-rw---- - - If unset, the Kubelet will not modify the ownership and permissions of any volume. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - fsGroupChangePolicy: - description: |- - fsGroupChangePolicy defines behavior of changing ownership and permission of the volume - before being exposed inside Pod. This field will only apply to - volume types which support fsGroup based ownership(and permissions). - It will have no effect on ephemeral volume types such as: secret, configmaps - and emptydir. - Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. - Note that this field cannot be set when spec.os.name is windows. - type: string - runAsGroup: - description: |- - The GID to run the entrypoint of the container process. - Uses runtime default if unset. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - runAsNonRoot: - description: |- - Indicates that the container must run as a non-root user. - If true, the Kubelet will validate the image at runtime to ensure that it - does not run as UID 0 (root) and fail to start the container if it does. - If unset or false, no such validation will be performed. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: |- - The UID to run the entrypoint of the container process. - Defaults to user specified in image metadata if unspecified. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - seLinuxOptions: - description: |- - The SELinux context to be applied to all containers. - If unspecified, the container runtime will allocate a random SELinux context for each - container. May also be set in SecurityContext. If set in - both SecurityContext and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. - Note that this field cannot be set when spec.os.name is windows. - type: object - properties: - level: - description: Level is SELinux level label that applies to the container. - type: string - role: - description: Role is a SELinux role label that applies to the container. - type: string - type: - description: Type is a SELinux type label that applies to the container. - type: string - user: - description: User is a SELinux user label that applies to the container. - type: string - seccompProfile: - description: |- - The seccomp options to use by the containers in this pod. - Note that this field cannot be set when spec.os.name is windows. - type: object - required: - - type - properties: - localhostProfile: - description: |- - localhostProfile indicates a profile defined in a file on the node should be used. - The profile must be preconfigured on the node to work. - Must be a descending path, relative to the kubelet's configured seccomp profile location. - Must be set if type is "Localhost". Must NOT be set for any other type. - type: string - type: - description: |- - type indicates which kind of seccomp profile will be applied. - Valid options are: - - Localhost - a profile defined in a file on the node should be used. - RuntimeDefault - the container runtime default profile should be used. - Unconfined - no profile should be applied. - type: string - supplementalGroups: - description: |- - A list of groups applied to the first process run in each container, in addition - to the container's primary GID, the fsGroup (if specified), and group memberships - defined in the container image for the uid of the container process. If unspecified, - no additional groups are added to any container. Note that group memberships - defined in the container image for the uid of the container process are still effective, - even if they are not included in this list. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - type: integer - format: int64 - sysctls: - description: |- - Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported - sysctls (by the container runtime) might fail to launch. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - description: Sysctl defines a kernel parameter to be set - type: object - required: - - name - - value - properties: - name: - description: Name of a property to set - type: string - value: - description: Value of a property to set - type: string - serviceAccountName: - description: If specified, the pod's service account - type: string - tolerations: - description: If specified, the pod's tolerations. - type: array - items: - description: |- - The pod this Toleration is attached to tolerates any taint that matches - the triple using the matching operator . - type: object - properties: - effect: - description: |- - Effect indicates the taint effect to match. Empty means match all taint effects. - When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: |- - Key is the taint key that the toleration applies to. Empty means match all taint keys. - If the key is empty, operator must be Exists; this combination means to match all values and all keys. - type: string - operator: - description: |- - Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. - Exists is equivalent to wildcard for value, so that a pod can - tolerate all taints of a particular category. - type: string - tolerationSeconds: - description: |- - TolerationSeconds represents the period of time the toleration (which must be - of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, - it is not set, which means tolerate the taint forever (do not evict). Zero and - negative values will be treated as 0 (evict immediately) by the system. - type: integer - format: int64 - value: - description: |- - Value is the taint value the toleration matches to. - If the operator is Exists, the value should be empty, otherwise just a regular string. - type: string - serviceType: - description: |- - Optional service type for Kubernetes solver service. Supported values - are NodePort or ClusterIP. If unset, defaults to NodePort. - type: string - selector: - description: |- - Selector selects a set of DNSNames on the Certificate resource that - should be solved using this challenge solver. - If not specified, the solver will be treated as the 'default' solver - with the lowest priority, i.e. if any other solver has a more specific - match, it will be used instead. - type: object - properties: - dnsNames: - description: |- - List of DNSNames that this solver will be used to solve. - If specified and a match is found, a dnsNames selector will take - precedence over a dnsZones selector. - If multiple solvers match with the same dnsNames value, the solver - with the most matching labels in matchLabels will be selected. - If neither has more matches, the solver defined earlier in the list - will be selected. - type: array - items: - type: string - dnsZones: - description: |- - List of DNSZones that this solver will be used to solve. - The most specific DNS zone match specified here will take precedence - over other DNS zone matches, so a solver specifying sys.example.com - will be selected over one specifying example.com for the domain - www.sys.example.com. - If multiple solvers match with the same dnsZones value, the solver - with the most matching labels in matchLabels will be selected. - If neither has more matches, the solver defined earlier in the list - will be selected. - type: array - items: - type: string - matchLabels: - description: |- - A label selector that is used to refine the set of certificate's that - this challenge solver will apply to. - type: object - additionalProperties: - type: string - token: - description: |- - The ACME challenge token for this challenge. - This is the raw value returned from the ACME server. - type: string - type: - description: |- - The type of ACME challenge this resource represents. - One of "HTTP-01" or "DNS-01". - type: string - enum: - - HTTP-01 - - DNS-01 - url: - description: |- - The URL of the ACME Challenge resource for this challenge. - This can be used to lookup details about the status of this challenge. - type: string - wildcard: - description: |- - wildcard will be true if this challenge is for a wildcard identifier, - for example '*.example.com'. - type: boolean - status: - type: object - properties: - presented: - description: |- - presented will be set to true if the challenge values for this challenge - are currently 'presented'. - This *does not* imply the self check is passing. Only that the values - have been 'submitted' for the appropriate challenge mechanism (i.e. the - DNS01 TXT record has been presented, or the HTTP01 configuration has been - configured). - type: boolean - processing: - description: |- - Used to denote whether this challenge should be processed or not. - This field will only be set to true by the 'scheduling' component. - It will only be set to false by the 'challenges' controller, after the - challenge has reached a final state or timed out. - If this field is set to false, the challenge controller will not take - any more action. - type: boolean - reason: - description: |- - Contains human readable information on why the Challenge is in the - current state. - type: string - state: - description: |- - Contains the current 'state' of the challenge. - If not set, the state of the challenge is unknown. - type: string - enum: - - valid - - ready - - pending - - processing - - invalid - - expired - - errored - served: true - storage: true - subresources: - status: {} - -# END crd {{- end }} - ---- -# START crd {{- if or .Values.crds.enabled .Values.installCRDs }} -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: clusterissuers.cert-manager.io - # START annotations {{- if .Values.crds.keep }} - annotations: - helm.sh/resource-policy: keep - # END annotations {{- end }} - labels: - app: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/name: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/instance: '{{ .Release.Name }}' - # Generated labels {{- include "labels" . | nindent 4 }} -spec: - group: cert-manager.io - names: - kind: ClusterIssuer - listKind: ClusterIssuerList - plural: clusterissuers - singular: clusterissuer - categories: - - cert-manager - scope: Cluster - versions: - - name: v1 - subresources: - status: {} - additionalPrinterColumns: - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - priority: 1 - type: string - - jsonPath: .metadata.creationTimestamp - description: CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC. - name: Age - type: date - schema: - openAPIV3Schema: - description: |- - A ClusterIssuer represents a certificate issuing authority which can be - referenced as part of `issuerRef` fields. - It is similar to an Issuer, however it is cluster-scoped and therefore can - be referenced by resources that exist in *any* namespace, not just the same - namespace as the referent. - type: object - required: - - spec - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: Desired state of the ClusterIssuer resource. - type: object - properties: - acme: - description: |- - ACME configures this issuer to communicate with a RFC8555 (ACME) server - to obtain signed x509 certificates. - type: object - required: - - privateKeySecretRef - - server - properties: - caBundle: - description: |- - Base64-encoded bundle of PEM CAs which can be used to validate the certificate - chain presented by the ACME server. - Mutually exclusive with SkipTLSVerify; prefer using CABundle to prevent various - kinds of security vulnerabilities. - If CABundle and SkipTLSVerify are unset, the system certificate bundle inside - the container is used to validate the TLS connection. - type: string - format: byte - disableAccountKeyGeneration: - description: |- - Enables or disables generating a new ACME account key. - If true, the Issuer resource will *not* request a new account but will expect - the account key to be supplied via an existing secret. - If false, the cert-manager system will generate a new ACME account key - for the Issuer. - Defaults to false. - type: boolean - email: - description: |- - Email is the email address to be associated with the ACME account. - This field is optional, but it is strongly recommended to be set. - It will be used to contact you in case of issues with your account or - certificates, including expiry notification emails. - This field may be updated after the account is initially registered. - type: string - enableDurationFeature: - description: |- - Enables requesting a Not After date on certificates that matches the - duration of the certificate. This is not supported by all ACME servers - like Let's Encrypt. If set to true when the ACME server does not support - it, it will create an error on the Order. - Defaults to false. - type: boolean - externalAccountBinding: - description: |- - ExternalAccountBinding is a reference to a CA external account of the ACME - server. - If set, upon registration cert-manager will attempt to associate the given - external account credentials with the registered ACME account. - type: object - required: - - keyID - - keySecretRef - properties: - keyAlgorithm: - description: |- - Deprecated: keyAlgorithm field exists for historical compatibility - reasons and should not be used. The algorithm is now hardcoded to HS256 - in golang/x/crypto/acme. - type: string - enum: - - HS256 - - HS384 - - HS512 - keyID: - description: keyID is the ID of the CA key that the External Account is bound to. - type: string - keySecretRef: - description: |- - keySecretRef is a Secret Key Selector referencing a data item in a Kubernetes - Secret which holds the symmetric MAC key of the External Account Binding. - The `key` is the index string that is paired with the key data in the - Secret and should not be confused with the key data itself, or indeed with - the External Account Binding keyID above. - The secret key stored in the Secret **must** be un-padded, base64 URL - encoded data. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - preferredChain: - description: |- - PreferredChain is the chain to use if the ACME server outputs multiple. - PreferredChain is no guarantee that this one gets delivered by the ACME - endpoint. - For example, for Let's Encrypt's DST crosssign you would use: - "DST Root CA X3" or "ISRG Root X1" for the newer Let's Encrypt root CA. - This value picks the first certificate bundle in the combined set of - ACME default and alternative chains that has a root-most certificate with - this value as its issuer's commonname. - type: string - maxLength: 64 - privateKeySecretRef: - description: |- - PrivateKey is the name of a Kubernetes Secret resource that will be used to - store the automatically generated ACME account private key. - Optionally, a `key` may be specified to select a specific entry within - the named Secret resource. - If `key` is not specified, a default of `tls.key` will be used. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - server: - description: |- - Server is the URL used to access the ACME server's 'directory' endpoint. - For example, for Let's Encrypt's staging endpoint, you would use: - "https://acme-staging-v02.api.letsencrypt.org/directory". - Only ACME v2 endpoints (i.e. RFC 8555) are supported. - type: string - skipTLSVerify: - description: |- - INSECURE: Enables or disables validation of the ACME server TLS certificate. - If true, requests to the ACME server will not have the TLS certificate chain - validated. - Mutually exclusive with CABundle; prefer using CABundle to prevent various - kinds of security vulnerabilities. - Only enable this option in development environments. - If CABundle and SkipTLSVerify are unset, the system certificate bundle inside - the container is used to validate the TLS connection. - Defaults to false. - type: boolean - solvers: - description: |- - Solvers is a list of challenge solvers that will be used to solve - ACME challenges for the matching domains. - Solver configurations must be provided in order to obtain certificates - from an ACME server. - For more information, see: https://cert-manager.io/docs/configuration/acme/ - type: array - items: - description: |- - An ACMEChallengeSolver describes how to solve ACME challenges for the issuer it is part of. - A selector may be provided to use different solving strategies for different DNS names. - Only one of HTTP01 or DNS01 must be provided. - type: object - properties: - dns01: - description: |- - Configures cert-manager to attempt to complete authorizations by - performing the DNS01 challenge flow. - type: object - properties: - acmeDNS: - description: |- - Use the 'ACME DNS' (https://github.com/joohoi/acme-dns) API to manage - DNS01 challenge records. - type: object - required: - - accountSecretRef - - host - properties: - accountSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - host: - type: string - akamai: - description: Use the Akamai DNS zone management API to manage DNS01 challenge records. - type: object - required: - - accessTokenSecretRef - - clientSecretSecretRef - - clientTokenSecretRef - - serviceConsumerDomain - properties: - accessTokenSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientSecretSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientTokenSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - serviceConsumerDomain: - type: string - azureDNS: - description: Use the Microsoft Azure DNS API to manage DNS01 challenge records. - type: object - required: - - resourceGroupName - - subscriptionID - properties: - clientID: - description: |- - Auth: Azure Service Principal: - The ClientID of the Azure Service Principal used to authenticate with Azure DNS. - If set, ClientSecret and TenantID must also be set. - type: string - clientSecretSecretRef: - description: |- - Auth: Azure Service Principal: - A reference to a Secret containing the password associated with the Service Principal. - If set, ClientID and TenantID must also be set. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - environment: - description: name of the Azure environment (default AzurePublicCloud) - type: string - enum: - - AzurePublicCloud - - AzureChinaCloud - - AzureGermanCloud - - AzureUSGovernmentCloud - hostedZoneName: - description: name of the DNS zone that should be used - type: string - managedIdentity: - description: |- - Auth: Azure Workload Identity or Azure Managed Service Identity: - Settings to enable Azure Workload Identity or Azure Managed Service Identity - If set, ClientID, ClientSecret and TenantID must not be set. - type: object - properties: - clientID: - description: client ID of the managed identity, can not be used at the same time as resourceID - type: string - resourceID: - description: |- - resource ID of the managed identity, can not be used at the same time as clientID - Cannot be used for Azure Managed Service Identity - type: string - tenantID: - description: tenant ID of the managed identity, can not be used at the same time as resourceID - type: string - resourceGroupName: - description: resource group the DNS zone is located in - type: string - subscriptionID: - description: ID of the Azure subscription - type: string - tenantID: - description: |- - Auth: Azure Service Principal: - The TenantID of the Azure Service Principal used to authenticate with Azure DNS. - If set, ClientID and ClientSecret must also be set. - type: string - cloudDNS: - description: Use the Google Cloud DNS API to manage DNS01 challenge records. - type: object - required: - - project - properties: - hostedZoneName: - description: |- - HostedZoneName is an optional field that tells cert-manager in which - Cloud DNS zone the challenge record has to be created. - If left empty cert-manager will automatically choose a zone. - type: string - project: - type: string - serviceAccountSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - cloudflare: - description: Use the Cloudflare API to manage DNS01 challenge records. - type: object - properties: - apiKeySecretRef: - description: |- - API key to use to authenticate with Cloudflare. - Note: using an API token to authenticate is now the recommended method - as it allows greater control of permissions. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - apiTokenSecretRef: - description: API token used to authenticate with Cloudflare. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - email: - description: Email of the account, only required when using API key based authentication. - type: string - cnameStrategy: - description: |- - CNAMEStrategy configures how the DNS01 provider should handle CNAME - records when found in DNS zones. - type: string - enum: - - None - - Follow - digitalocean: - description: Use the DigitalOcean DNS API to manage DNS01 challenge records. - type: object - required: - - tokenSecretRef - properties: - tokenSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - rfc2136: - description: |- - Use RFC2136 ("Dynamic Updates in the Domain Name System") (https://datatracker.ietf.org/doc/rfc2136/) - to manage DNS01 challenge records. - type: object - required: - - nameserver - properties: - nameserver: - description: |- - The IP address or hostname of an authoritative DNS server supporting - RFC2136 in the form host:port. If the host is an IPv6 address it must be - enclosed in square brackets (e.g [2001:db8::1]) ; port is optional. - This field is required. - type: string - tsigAlgorithm: - description: |- - The TSIG Algorithm configured in the DNS supporting RFC2136. Used only - when ``tsigSecretSecretRef`` and ``tsigKeyName`` are defined. - Supported values are (case-insensitive): ``HMACMD5`` (default), - ``HMACSHA1``, ``HMACSHA256`` or ``HMACSHA512``. - type: string - tsigKeyName: - description: |- - The TSIG Key name configured in the DNS. - If ``tsigSecretSecretRef`` is defined, this field is required. - type: string - tsigSecretSecretRef: - description: |- - The name of the secret containing the TSIG value. - If ``tsigKeyName`` is defined, this field is required. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - route53: - description: Use the AWS Route53 API to manage DNS01 challenge records. - type: object - properties: - accessKeyID: - description: |- - The AccessKeyID is used for authentication. - Cannot be set when SecretAccessKeyID is set. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, - see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials - type: string - accessKeyIDSecretRef: - description: |- - The SecretAccessKey is used for authentication. If set, pull the AWS - access key ID from a key within a Kubernetes Secret. - Cannot be set when AccessKeyID is set. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, - see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - auth: - description: Auth configures how cert-manager authenticates. - type: object - required: - - kubernetes - properties: - kubernetes: - description: |- - Kubernetes authenticates with Route53 using AssumeRoleWithWebIdentity - by passing a bound ServiceAccount token. - type: object - required: - - serviceAccountRef - properties: - serviceAccountRef: - description: |- - A reference to a service account that will be used to request a bound - token (also known as "projected token"). To use this field, you must - configure an RBAC rule to let cert-manager request a token. - type: object - required: - - name - properties: - audiences: - description: |- - TokenAudiences is an optional list of audiences to include in the - token passed to AWS. The default token consisting of the issuer's namespace - and name is always included. - If unset the audience defaults to `sts.amazonaws.com`. - type: array - items: - type: string - name: - description: Name of the ServiceAccount used to request a token. - type: string - hostedZoneID: - description: If set, the provider will manage only this zone in Route53 and will not do a lookup using the route53:ListHostedZonesByName api call. - type: string - region: - description: |- - Override the AWS region. - - Route53 is a global service and does not have regional endpoints but the - region specified here (or via environment variables) is used as a hint to - help compute the correct AWS credential scope and partition when it - connects to Route53. See: - - [Amazon Route 53 endpoints and quotas](https://docs.aws.amazon.com/general/latest/gr/r53.html) - - [Global services](https://docs.aws.amazon.com/whitepapers/latest/aws-fault-isolation-boundaries/global-services.html) - - If you omit this region field, cert-manager will use the region from - AWS_REGION and AWS_DEFAULT_REGION environment variables, if they are set - in the cert-manager controller Pod. - - The `region` field is not needed if you use [IAM Roles for Service Accounts (IRSA)](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html). - Instead an AWS_REGION environment variable is added to the cert-manager controller Pod by: - [Amazon EKS Pod Identity Webhook](https://github.com/aws/amazon-eks-pod-identity-webhook). - In this case this `region` field value is ignored. - - The `region` field is not needed if you use [EKS Pod Identities](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html). - Instead an AWS_REGION environment variable is added to the cert-manager controller Pod by: - [Amazon EKS Pod Identity Agent](https://github.com/aws/eks-pod-identity-agent), - In this case this `region` field value is ignored. - type: string - role: - description: |- - Role is a Role ARN which the Route53 provider will assume using either the explicit credentials AccessKeyID/SecretAccessKey - or the inferred credentials from environment variables, shared credentials file or AWS Instance metadata - type: string - secretAccessKeySecretRef: - description: |- - The SecretAccessKey is used for authentication. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, - see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - webhook: - description: |- - Configure an external webhook based DNS01 challenge solver to manage - DNS01 challenge records. - type: object - required: - - groupName - - solverName - properties: - config: - description: |- - Additional configuration that should be passed to the webhook apiserver - when challenges are processed. - This can contain arbitrary JSON data. - Secret values should not be specified in this stanza. - If secret values are needed (e.g. credentials for a DNS service), you - should use a SecretKeySelector to reference a Secret resource. - For details on the schema of this field, consult the webhook provider - implementation's documentation. - x-kubernetes-preserve-unknown-fields: true - groupName: - description: |- - The API group name that should be used when POSTing ChallengePayload - resources to the webhook apiserver. - This should be the same as the GroupName specified in the webhook - provider implementation. - type: string - solverName: - description: |- - The name of the solver to use, as defined in the webhook provider - implementation. - This will typically be the name of the provider, e.g. 'cloudflare'. - type: string - http01: - description: |- - Configures cert-manager to attempt to complete authorizations by - performing the HTTP01 challenge flow. - It is not possible to obtain certificates for wildcard domain names - (e.g. `*.example.com`) using the HTTP01 challenge mechanism. - type: object - properties: - gatewayHTTPRoute: - description: |- - The Gateway API is a sig-network community API that models service networking - in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will - create HTTPRoutes with the specified labels in the same namespace as the challenge. - This solver is experimental, and fields / behaviour may change in the future. - type: object - properties: - labels: - description: |- - Custom labels that will be applied to HTTPRoutes created by cert-manager - while solving HTTP-01 challenges. - type: object - additionalProperties: - type: string - parentRefs: - description: |- - When solving an HTTP-01 challenge, cert-manager creates an HTTPRoute. - cert-manager needs to know which parentRefs should be used when creating - the HTTPRoute. Usually, the parentRef references a Gateway. See: - https://gateway-api.sigs.k8s.io/api-types/httproute/#attaching-to-gateways - type: array - items: - description: |- - ParentReference identifies an API object (usually a Gateway) that can be considered - a parent of this resource (usually a route). There are two kinds of parent resources - with "Core" support: - - * Gateway (Gateway conformance profile) - * Service (Mesh conformance profile, ClusterIP Services only) - - This API may be extended in the future to support additional kinds of parent - resources. - - The API object must be valid in the cluster; the Group and Kind must - be registered in the cluster for this reference to be valid. - type: object - required: - - name - properties: - group: - description: |- - Group is the group of the referent. - When unspecified, "gateway.networking.k8s.io" is inferred. - To set the core API group (such as for a "Service" kind referent), - Group must be explicitly set to "" (empty string). - - Support: Core - type: string - default: gateway.networking.k8s.io - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - kind: - description: |- - Kind is kind of the referent. - - There are two kinds of parent resources with "Core" support: - - * Gateway (Gateway conformance profile) - * Service (Mesh conformance profile, ClusterIP Services only) - - Support for other resources is Implementation-Specific. - type: string - default: Gateway - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - name: - description: |- - Name is the name of the referent. - - Support: Core - type: string - maxLength: 253 - minLength: 1 - namespace: - description: |- - Namespace is the namespace of the referent. When unspecified, this refers - to the local namespace of the Route. - - Note that there are specific rules for ParentRefs which cross namespace - boundaries. Cross-namespace references are only valid if they are explicitly - allowed by something in the namespace they are referring to. For example: - Gateway has the AllowedRoutes field, and ReferenceGrant provides a - generic way to enable any other kind of cross-namespace reference. - - - ParentRefs from a Route to a Service in the same namespace are "producer" - routes, which apply default routing rules to inbound connections from - any namespace to the Service. - - ParentRefs from a Route to a Service in a different namespace are - "consumer" routes, and these routing rules are only applied to outbound - connections originating from the same namespace as the Route, for which - the intended destination of the connections are a Service targeted as a - ParentRef of the Route. - - - Support: Core - type: string - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - port: - description: |- - Port is the network port this Route targets. It can be interpreted - differently based on the type of parent resource. - - When the parent resource is a Gateway, this targets all listeners - listening on the specified port that also support this kind of Route(and - select this Route). It's not recommended to set `Port` unless the - networking behaviors specified in a Route must apply to a specific port - as opposed to a listener(s) whose port(s) may be changed. When both Port - and SectionName are specified, the name and port of the selected listener - must match both specified values. - - - When the parent resource is a Service, this targets a specific port in the - Service spec. When both Port (experimental) and SectionName are specified, - the name and port of the selected port must match both specified values. - - - Implementations MAY choose to support other parent resources. - Implementations supporting other types of parent resources MUST clearly - document how/if Port is interpreted. - - For the purpose of status, an attachment is considered successful as - long as the parent resource accepts it partially. For example, Gateway - listeners can restrict which Routes can attach to them by Route kind, - namespace, or hostname. If 1 of 2 Gateway listeners accept attachment - from the referencing Route, the Route MUST be considered successfully - attached. If no Gateway listeners accept attachment from this Route, - the Route MUST be considered detached from the Gateway. - - Support: Extended - type: integer - format: int32 - maximum: 65535 - minimum: 1 - sectionName: - description: |- - SectionName is the name of a section within the target resource. In the - following resources, SectionName is interpreted as the following: - - * Gateway: Listener name. When both Port (experimental) and SectionName - are specified, the name and port of the selected listener must match - both specified values. - * Service: Port name. When both Port (experimental) and SectionName - are specified, the name and port of the selected listener must match - both specified values. - - Implementations MAY choose to support attaching Routes to other resources. - If that is the case, they MUST clearly document how SectionName is - interpreted. - - When unspecified (empty string), this will reference the entire resource. - For the purpose of status, an attachment is considered successful if at - least one section in the parent resource accepts it. For example, Gateway - listeners can restrict which Routes can attach to them by Route kind, - namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from - the referencing Route, the Route MUST be considered successfully - attached. If no Gateway listeners accept attachment from this Route, the - Route MUST be considered detached from the Gateway. - - Support: Core - type: string - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - podTemplate: - description: |- - Optional pod template used to configure the ACME challenge solver pods - used for HTTP01 challenges. - type: object - properties: - metadata: - description: |- - ObjectMeta overrides for the pod used to solve HTTP01 challenges. - Only the 'labels' and 'annotations' fields may be set. - If labels or annotations overlap with in-built values, the values here - will override the in-built values. - type: object - properties: - annotations: - description: Annotations that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - labels: - description: Labels that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - spec: - description: |- - PodSpec defines overrides for the HTTP01 challenge solver pod. - Check ACMEChallengeSolverHTTP01IngressPodSpec to find out currently supported fields. - All other fields will be ignored. - type: object - properties: - affinity: - description: If specified, the pod's scheduling constraints - type: object - properties: - nodeAffinity: - description: Describes node affinity scheduling rules for the pod. - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node matches the corresponding matchExpressions; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: |- - An empty preferred scheduling term matches all objects with implicit weight 0 - (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). - type: object - required: - - preference - - weight - properties: - preference: - description: A node selector term, associated with the corresponding weight. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - weight: - description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to an update), the system - may or may not try to eventually evict the pod from its node. - type: object - required: - - nodeSelectorTerms - properties: - nodeSelectorTerms: - description: Required. A list of node selector terms. The terms are ORed. - type: array - items: - description: |- - A null or empty node selector term matches no objects. The requirements of - them are ANDed. - The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - podAffinity: - description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - podAntiAffinity: - description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the anti-affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the anti-affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the anti-affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - imagePullSecrets: - description: If specified, the pod's imagePullSecrets - type: array - items: - description: |- - LocalObjectReference contains enough information to let you locate the - referenced object inside the same namespace. - type: object - properties: - name: - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - default: "" - x-kubernetes-map-type: atomic - nodeSelector: - description: |- - NodeSelector is a selector which must be true for the pod to fit on a node. - Selector which must match a node's labels for the pod to be scheduled on that node. - More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ - type: object - additionalProperties: - type: string - priorityClassName: - description: If specified, the pod's priorityClassName. - type: string - securityContext: - description: If specified, the pod's security context - type: object - properties: - fsGroup: - description: |- - A special supplemental group that applies to all containers in a pod. - Some volume types allow the Kubelet to change the ownership of that volume - to be owned by the pod: - - 1. The owning GID will be the FSGroup - 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) - 3. The permission bits are OR'd with rw-rw---- - - If unset, the Kubelet will not modify the ownership and permissions of any volume. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - fsGroupChangePolicy: - description: |- - fsGroupChangePolicy defines behavior of changing ownership and permission of the volume - before being exposed inside Pod. This field will only apply to - volume types which support fsGroup based ownership(and permissions). - It will have no effect on ephemeral volume types such as: secret, configmaps - and emptydir. - Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. - Note that this field cannot be set when spec.os.name is windows. - type: string - runAsGroup: - description: |- - The GID to run the entrypoint of the container process. - Uses runtime default if unset. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - runAsNonRoot: - description: |- - Indicates that the container must run as a non-root user. - If true, the Kubelet will validate the image at runtime to ensure that it - does not run as UID 0 (root) and fail to start the container if it does. - If unset or false, no such validation will be performed. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: |- - The UID to run the entrypoint of the container process. - Defaults to user specified in image metadata if unspecified. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - seLinuxOptions: - description: |- - The SELinux context to be applied to all containers. - If unspecified, the container runtime will allocate a random SELinux context for each - container. May also be set in SecurityContext. If set in - both SecurityContext and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. - Note that this field cannot be set when spec.os.name is windows. - type: object - properties: - level: - description: Level is SELinux level label that applies to the container. - type: string - role: - description: Role is a SELinux role label that applies to the container. - type: string - type: - description: Type is a SELinux type label that applies to the container. - type: string - user: - description: User is a SELinux user label that applies to the container. - type: string - seccompProfile: - description: |- - The seccomp options to use by the containers in this pod. - Note that this field cannot be set when spec.os.name is windows. - type: object - required: - - type - properties: - localhostProfile: - description: |- - localhostProfile indicates a profile defined in a file on the node should be used. - The profile must be preconfigured on the node to work. - Must be a descending path, relative to the kubelet's configured seccomp profile location. - Must be set if type is "Localhost". Must NOT be set for any other type. - type: string - type: - description: |- - type indicates which kind of seccomp profile will be applied. - Valid options are: - - Localhost - a profile defined in a file on the node should be used. - RuntimeDefault - the container runtime default profile should be used. - Unconfined - no profile should be applied. - type: string - supplementalGroups: - description: |- - A list of groups applied to the first process run in each container, in addition - to the container's primary GID, the fsGroup (if specified), and group memberships - defined in the container image for the uid of the container process. If unspecified, - no additional groups are added to any container. Note that group memberships - defined in the container image for the uid of the container process are still effective, - even if they are not included in this list. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - type: integer - format: int64 - sysctls: - description: |- - Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported - sysctls (by the container runtime) might fail to launch. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - description: Sysctl defines a kernel parameter to be set - type: object - required: - - name - - value - properties: - name: - description: Name of a property to set - type: string - value: - description: Value of a property to set - type: string - serviceAccountName: - description: If specified, the pod's service account - type: string - tolerations: - description: If specified, the pod's tolerations. - type: array - items: - description: |- - The pod this Toleration is attached to tolerates any taint that matches - the triple using the matching operator . - type: object - properties: - effect: - description: |- - Effect indicates the taint effect to match. Empty means match all taint effects. - When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: |- - Key is the taint key that the toleration applies to. Empty means match all taint keys. - If the key is empty, operator must be Exists; this combination means to match all values and all keys. - type: string - operator: - description: |- - Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. - Exists is equivalent to wildcard for value, so that a pod can - tolerate all taints of a particular category. - type: string - tolerationSeconds: - description: |- - TolerationSeconds represents the period of time the toleration (which must be - of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, - it is not set, which means tolerate the taint forever (do not evict). Zero and - negative values will be treated as 0 (evict immediately) by the system. - type: integer - format: int64 - value: - description: |- - Value is the taint value the toleration matches to. - If the operator is Exists, the value should be empty, otherwise just a regular string. - type: string - serviceType: - description: |- - Optional service type for Kubernetes solver service. Supported values - are NodePort or ClusterIP. If unset, defaults to NodePort. - type: string - ingress: - description: |- - The ingress based HTTP01 challenge solver will solve challenges by - creating or modifying Ingress resources in order to route requests for - '/.well-known/acme-challenge/XYZ' to 'challenge solver' pods that are - provisioned by cert-manager for each Challenge to be completed. - type: object - properties: - class: - description: |- - This field configures the annotation `kubernetes.io/ingress.class` when - creating Ingress resources to solve ACME challenges that use this - challenge solver. Only one of `class`, `name` or `ingressClassName` may - be specified. - type: string - ingressClassName: - description: |- - This field configures the field `ingressClassName` on the created Ingress - resources used to solve ACME challenges that use this challenge solver. - This is the recommended way of configuring the ingress class. Only one of - `class`, `name` or `ingressClassName` may be specified. - type: string - ingressTemplate: - description: |- - Optional ingress template used to configure the ACME challenge solver - ingress used for HTTP01 challenges. - type: object - properties: - metadata: - description: |- - ObjectMeta overrides for the ingress used to solve HTTP01 challenges. - Only the 'labels' and 'annotations' fields may be set. - If labels or annotations overlap with in-built values, the values here - will override the in-built values. - type: object - properties: - annotations: - description: Annotations that should be added to the created ACME HTTP01 solver ingress. - type: object - additionalProperties: - type: string - labels: - description: Labels that should be added to the created ACME HTTP01 solver ingress. - type: object - additionalProperties: - type: string - name: - description: |- - The name of the ingress resource that should have ACME challenge solving - routes inserted into it in order to solve HTTP01 challenges. - This is typically used in conjunction with ingress controllers like - ingress-gce, which maintains a 1:1 mapping between external IPs and - ingress resources. Only one of `class`, `name` or `ingressClassName` may - be specified. - type: string - podTemplate: - description: |- - Optional pod template used to configure the ACME challenge solver pods - used for HTTP01 challenges. - type: object - properties: - metadata: - description: |- - ObjectMeta overrides for the pod used to solve HTTP01 challenges. - Only the 'labels' and 'annotations' fields may be set. - If labels or annotations overlap with in-built values, the values here - will override the in-built values. - type: object - properties: - annotations: - description: Annotations that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - labels: - description: Labels that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - spec: - description: |- - PodSpec defines overrides for the HTTP01 challenge solver pod. - Check ACMEChallengeSolverHTTP01IngressPodSpec to find out currently supported fields. - All other fields will be ignored. - type: object - properties: - affinity: - description: If specified, the pod's scheduling constraints - type: object - properties: - nodeAffinity: - description: Describes node affinity scheduling rules for the pod. - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node matches the corresponding matchExpressions; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: |- - An empty preferred scheduling term matches all objects with implicit weight 0 - (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). - type: object - required: - - preference - - weight - properties: - preference: - description: A node selector term, associated with the corresponding weight. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - weight: - description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to an update), the system - may or may not try to eventually evict the pod from its node. - type: object - required: - - nodeSelectorTerms - properties: - nodeSelectorTerms: - description: Required. A list of node selector terms. The terms are ORed. - type: array - items: - description: |- - A null or empty node selector term matches no objects. The requirements of - them are ANDed. - The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - podAffinity: - description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - podAntiAffinity: - description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the anti-affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the anti-affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the anti-affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - imagePullSecrets: - description: If specified, the pod's imagePullSecrets - type: array - items: - description: |- - LocalObjectReference contains enough information to let you locate the - referenced object inside the same namespace. - type: object - properties: - name: - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - default: "" - x-kubernetes-map-type: atomic - nodeSelector: - description: |- - NodeSelector is a selector which must be true for the pod to fit on a node. - Selector which must match a node's labels for the pod to be scheduled on that node. - More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ - type: object - additionalProperties: - type: string - priorityClassName: - description: If specified, the pod's priorityClassName. - type: string - securityContext: - description: If specified, the pod's security context - type: object - properties: - fsGroup: - description: |- - A special supplemental group that applies to all containers in a pod. - Some volume types allow the Kubelet to change the ownership of that volume - to be owned by the pod: - - 1. The owning GID will be the FSGroup - 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) - 3. The permission bits are OR'd with rw-rw---- - - If unset, the Kubelet will not modify the ownership and permissions of any volume. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - fsGroupChangePolicy: - description: |- - fsGroupChangePolicy defines behavior of changing ownership and permission of the volume - before being exposed inside Pod. This field will only apply to - volume types which support fsGroup based ownership(and permissions). - It will have no effect on ephemeral volume types such as: secret, configmaps - and emptydir. - Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. - Note that this field cannot be set when spec.os.name is windows. - type: string - runAsGroup: - description: |- - The GID to run the entrypoint of the container process. - Uses runtime default if unset. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - runAsNonRoot: - description: |- - Indicates that the container must run as a non-root user. - If true, the Kubelet will validate the image at runtime to ensure that it - does not run as UID 0 (root) and fail to start the container if it does. - If unset or false, no such validation will be performed. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: |- - The UID to run the entrypoint of the container process. - Defaults to user specified in image metadata if unspecified. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - seLinuxOptions: - description: |- - The SELinux context to be applied to all containers. - If unspecified, the container runtime will allocate a random SELinux context for each - container. May also be set in SecurityContext. If set in - both SecurityContext and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. - Note that this field cannot be set when spec.os.name is windows. - type: object - properties: - level: - description: Level is SELinux level label that applies to the container. - type: string - role: - description: Role is a SELinux role label that applies to the container. - type: string - type: - description: Type is a SELinux type label that applies to the container. - type: string - user: - description: User is a SELinux user label that applies to the container. - type: string - seccompProfile: - description: |- - The seccomp options to use by the containers in this pod. - Note that this field cannot be set when spec.os.name is windows. - type: object - required: - - type - properties: - localhostProfile: - description: |- - localhostProfile indicates a profile defined in a file on the node should be used. - The profile must be preconfigured on the node to work. - Must be a descending path, relative to the kubelet's configured seccomp profile location. - Must be set if type is "Localhost". Must NOT be set for any other type. - type: string - type: - description: |- - type indicates which kind of seccomp profile will be applied. - Valid options are: - - Localhost - a profile defined in a file on the node should be used. - RuntimeDefault - the container runtime default profile should be used. - Unconfined - no profile should be applied. - type: string - supplementalGroups: - description: |- - A list of groups applied to the first process run in each container, in addition - to the container's primary GID, the fsGroup (if specified), and group memberships - defined in the container image for the uid of the container process. If unspecified, - no additional groups are added to any container. Note that group memberships - defined in the container image for the uid of the container process are still effective, - even if they are not included in this list. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - type: integer - format: int64 - sysctls: - description: |- - Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported - sysctls (by the container runtime) might fail to launch. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - description: Sysctl defines a kernel parameter to be set - type: object - required: - - name - - value - properties: - name: - description: Name of a property to set - type: string - value: - description: Value of a property to set - type: string - serviceAccountName: - description: If specified, the pod's service account - type: string - tolerations: - description: If specified, the pod's tolerations. - type: array - items: - description: |- - The pod this Toleration is attached to tolerates any taint that matches - the triple using the matching operator . - type: object - properties: - effect: - description: |- - Effect indicates the taint effect to match. Empty means match all taint effects. - When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: |- - Key is the taint key that the toleration applies to. Empty means match all taint keys. - If the key is empty, operator must be Exists; this combination means to match all values and all keys. - type: string - operator: - description: |- - Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. - Exists is equivalent to wildcard for value, so that a pod can - tolerate all taints of a particular category. - type: string - tolerationSeconds: - description: |- - TolerationSeconds represents the period of time the toleration (which must be - of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, - it is not set, which means tolerate the taint forever (do not evict). Zero and - negative values will be treated as 0 (evict immediately) by the system. - type: integer - format: int64 - value: - description: |- - Value is the taint value the toleration matches to. - If the operator is Exists, the value should be empty, otherwise just a regular string. - type: string - serviceType: - description: |- - Optional service type for Kubernetes solver service. Supported values - are NodePort or ClusterIP. If unset, defaults to NodePort. - type: string - selector: - description: |- - Selector selects a set of DNSNames on the Certificate resource that - should be solved using this challenge solver. - If not specified, the solver will be treated as the 'default' solver - with the lowest priority, i.e. if any other solver has a more specific - match, it will be used instead. - type: object - properties: - dnsNames: - description: |- - List of DNSNames that this solver will be used to solve. - If specified and a match is found, a dnsNames selector will take - precedence over a dnsZones selector. - If multiple solvers match with the same dnsNames value, the solver - with the most matching labels in matchLabels will be selected. - If neither has more matches, the solver defined earlier in the list - will be selected. - type: array - items: - type: string - dnsZones: - description: |- - List of DNSZones that this solver will be used to solve. - The most specific DNS zone match specified here will take precedence - over other DNS zone matches, so a solver specifying sys.example.com - will be selected over one specifying example.com for the domain - www.sys.example.com. - If multiple solvers match with the same dnsZones value, the solver - with the most matching labels in matchLabels will be selected. - If neither has more matches, the solver defined earlier in the list - will be selected. - type: array - items: - type: string - matchLabels: - description: |- - A label selector that is used to refine the set of certificate's that - this challenge solver will apply to. - type: object - additionalProperties: - type: string - ca: - description: |- - CA configures this issuer to sign certificates using a signing CA keypair - stored in a Secret resource. - This is used to build internal PKIs that are managed by cert-manager. - type: object - required: - - secretName - properties: - crlDistributionPoints: - description: |- - The CRL distribution points is an X.509 v3 certificate extension which identifies - the location of the CRL from which the revocation of this certificate can be checked. - If not set, certificates will be issued without distribution points set. - type: array - items: - type: string - issuingCertificateURLs: - description: |- - IssuingCertificateURLs is a list of URLs which this issuer should embed into certificates - it creates. See https://www.rfc-editor.org/rfc/rfc5280#section-4.2.2.1 for more details. - As an example, such a URL might be "http://ca.domain.com/ca.crt". - type: array - items: - type: string - ocspServers: - description: |- - The OCSP server list is an X.509 v3 extension that defines a list of - URLs of OCSP responders. The OCSP responders can be queried for the - revocation status of an issued certificate. If not set, the - certificate will be issued with no OCSP servers set. For example, an - OCSP server URL could be "http://ocsp.int-x3.letsencrypt.org". - type: array - items: - type: string - secretName: - description: |- - SecretName is the name of the secret used to sign Certificates issued - by this Issuer. - type: string - selfSigned: - description: |- - SelfSigned configures this issuer to 'self sign' certificates using the - private key used to create the CertificateRequest object. - type: object - properties: - crlDistributionPoints: - description: |- - The CRL distribution points is an X.509 v3 certificate extension which identifies - the location of the CRL from which the revocation of this certificate can be checked. - If not set certificate will be issued without CDP. Values are strings. - type: array - items: - type: string - vault: - description: |- - Vault configures this issuer to sign certificates using a HashiCorp Vault - PKI backend. - type: object - required: - - auth - - path - - server - properties: - auth: - description: Auth configures how cert-manager authenticates with the Vault server. - type: object - properties: - appRole: - description: |- - AppRole authenticates with Vault using the App Role auth mechanism, - with the role and secret stored in a Kubernetes Secret resource. - type: object - required: - - path - - roleId - - secretRef - properties: - path: - description: |- - Path where the App Role authentication backend is mounted in Vault, e.g: - "approle" - type: string - roleId: - description: |- - RoleID configured in the App Role authentication backend when setting - up the authentication backend in Vault. - type: string - secretRef: - description: |- - Reference to a key in a Secret that contains the App Role secret used - to authenticate with Vault. - The `key` field must be specified and denotes which entry within the Secret - resource is used as the app role secret. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientCertificate: - description: |- - ClientCertificate authenticates with Vault by presenting a client - certificate during the request's TLS handshake. - Works only when using HTTPS protocol. - type: object - properties: - mountPath: - description: |- - The Vault mountPath here is the mount path to use when authenticating with - Vault. For example, setting a value to `/v1/auth/foo`, will use the path - `/v1/auth/foo/login` to authenticate with Vault. If unspecified, the - default value "/v1/auth/cert" will be used. - type: string - name: - description: |- - Name of the certificate role to authenticate against. - If not set, matching any certificate role, if available. - type: string - secretName: - description: |- - Reference to Kubernetes Secret of type "kubernetes.io/tls" (hence containing - tls.crt and tls.key) used to authenticate to Vault using TLS client - authentication. - type: string - kubernetes: - description: |- - Kubernetes authenticates with Vault by passing the ServiceAccount - token stored in the named Secret resource to the Vault server. - type: object - required: - - role - properties: - mountPath: - description: |- - The Vault mountPath here is the mount path to use when authenticating with - Vault. For example, setting a value to `/v1/auth/foo`, will use the path - `/v1/auth/foo/login` to authenticate with Vault. If unspecified, the - default value "/v1/auth/kubernetes" will be used. - type: string - role: - description: |- - A required field containing the Vault Role to assume. A Role binds a - Kubernetes ServiceAccount with a set of Vault policies. - type: string - secretRef: - description: |- - The required Secret field containing a Kubernetes ServiceAccount JWT used - for authenticating with Vault. Use of 'ambient credentials' is not - supported. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - serviceAccountRef: - description: |- - A reference to a service account that will be used to request a bound - token (also known as "projected token"). Compared to using "secretRef", - using this field means that you don't rely on statically bound tokens. To - use this field, you must configure an RBAC rule to let cert-manager - request a token. - type: object - required: - - name - properties: - audiences: - description: |- - TokenAudiences is an optional list of extra audiences to include in the token passed to Vault. The default token - consisting of the issuer's namespace and name is always included. - type: array - items: - type: string - name: - description: Name of the ServiceAccount used to request a token. - type: string - tokenSecretRef: - description: TokenSecretRef authenticates with Vault by presenting a token. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - caBundle: - description: |- - Base64-encoded bundle of PEM CAs which will be used to validate the certificate - chain presented by Vault. Only used if using HTTPS to connect to Vault and - ignored for HTTP connections. - Mutually exclusive with CABundleSecretRef. - If neither CABundle nor CABundleSecretRef are defined, the certificate bundle in - the cert-manager controller container is used to validate the TLS connection. - type: string - format: byte - caBundleSecretRef: - description: |- - Reference to a Secret containing a bundle of PEM-encoded CAs to use when - verifying the certificate chain presented by Vault when using HTTPS. - Mutually exclusive with CABundle. - If neither CABundle nor CABundleSecretRef are defined, the certificate bundle in - the cert-manager controller container is used to validate the TLS connection. - If no key for the Secret is specified, cert-manager will default to 'ca.crt'. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientCertSecretRef: - description: |- - Reference to a Secret containing a PEM-encoded Client Certificate to use when the - Vault server requires mTLS. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientKeySecretRef: - description: |- - Reference to a Secret containing a PEM-encoded Client Private Key to use when the - Vault server requires mTLS. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - namespace: - description: |- - Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1" - More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces - type: string - path: - description: |- - Path is the mount path of the Vault PKI backend's `sign` endpoint, e.g: - "my_pki_mount/sign/my-role-name". - type: string - server: - description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' - type: string - venafi: - description: |- - Venafi configures this issuer to sign certificates using a Venafi TPP - or Venafi Cloud policy zone. - type: object - required: - - zone - properties: - cloud: - description: |- - Cloud specifies the Venafi cloud configuration settings. - Only one of TPP or Cloud may be specified. - type: object - required: - - apiTokenSecretRef - properties: - apiTokenSecretRef: - description: APITokenSecretRef is a secret key selector for the Venafi Cloud API token. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - url: - description: |- - URL is the base URL for Venafi Cloud. - Defaults to "https://api.venafi.cloud/v1". - type: string - tpp: - description: |- - TPP specifies Trust Protection Platform configuration settings. - Only one of TPP or Cloud may be specified. - type: object - required: - - credentialsRef - - url - properties: - caBundle: - description: |- - Base64-encoded bundle of PEM CAs which will be used to validate the certificate - chain presented by the TPP server. Only used if using HTTPS; ignored for HTTP. - If undefined, the certificate bundle in the cert-manager controller container - is used to validate the chain. - type: string - format: byte - caBundleSecretRef: - description: |- - Reference to a Secret containing a base64-encoded bundle of PEM CAs - which will be used to validate the certificate chain presented by the TPP server. - Only used if using HTTPS; ignored for HTTP. Mutually exclusive with CABundle. - If neither CABundle nor CABundleSecretRef is defined, the certificate bundle in - the cert-manager controller container is used to validate the TLS connection. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - credentialsRef: - description: |- - CredentialsRef is a reference to a Secret containing the Venafi TPP API credentials. - The secret must contain the key 'access-token' for the Access Token Authentication, - or two keys, 'username' and 'password' for the API Keys Authentication. - type: object - required: - - name - properties: - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - url: - description: |- - URL is the base URL for the vedsdk endpoint of the Venafi TPP instance, - for example: "https://tpp.example.com/vedsdk". - type: string - zone: - description: |- - Zone is the Venafi Policy Zone to use for this issuer. - All requests made to the Venafi platform will be restricted by the named - zone policy. - This field is required. - type: string - status: - description: Status of the ClusterIssuer. This is set and managed automatically. - type: object - properties: - acme: - description: |- - ACME specific status options. - This field should only be set if the Issuer is configured to use an ACME - server to issue certificates. - type: object - properties: - lastPrivateKeyHash: - description: |- - LastPrivateKeyHash is a hash of the private key associated with the latest - registered ACME account, in order to track changes made to registered account - associated with the Issuer - type: string - lastRegisteredEmail: - description: |- - LastRegisteredEmail is the email associated with the latest registered - ACME account, in order to track changes made to registered account - associated with the Issuer - type: string - uri: - description: |- - URI is the unique account identifier, which can also be used to retrieve - account details from the CA - type: string - conditions: - description: |- - List of status conditions to indicate the status of a CertificateRequest. - Known condition types are `Ready`. - type: array - items: - description: IssuerCondition contains condition information for an Issuer. - type: object - required: - - status - - type - properties: - lastTransitionTime: - description: |- - LastTransitionTime is the timestamp corresponding to the last status - change of this condition. - type: string - format: date-time - message: - description: |- - Message is a human readable description of the details of the last - transition, complementing reason. - type: string - observedGeneration: - description: |- - If set, this represents the .metadata.generation that the condition was - set based upon. - For instance, if .metadata.generation is currently 12, but the - .status.condition[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the Issuer. - type: integer - format: int64 - reason: - description: |- - Reason is a brief machine readable explanation for the condition's last - transition. - type: string - status: - description: Status of the condition, one of (`True`, `False`, `Unknown`). - type: string - enum: - - "True" - - "False" - - Unknown - type: - description: Type of the condition, known values are (`Ready`). - type: string - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - served: true - storage: true - -# END crd {{- end }} - ---- -# START crd {{- if or .Values.crds.enabled .Values.installCRDs }} -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: issuers.cert-manager.io - # START annotations {{- if .Values.crds.keep }} - annotations: - helm.sh/resource-policy: keep - # END annotations {{- end }} - labels: - app: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/name: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/instance: '{{ .Release.Name }}' - app.kubernetes.io/component: "crds" - # Generated labels {{- include "labels" . | nindent 4 }} -spec: - group: cert-manager.io - names: - kind: Issuer - listKind: IssuerList - plural: issuers - singular: issuer - categories: - - cert-manager - scope: Namespaced - versions: - - name: v1 - subresources: - status: {} - additionalPrinterColumns: - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - priority: 1 - type: string - - jsonPath: .metadata.creationTimestamp - description: CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC. - name: Age - type: date - schema: - openAPIV3Schema: - description: |- - An Issuer represents a certificate issuing authority which can be - referenced as part of `issuerRef` fields. - It is scoped to a single namespace and can therefore only be referenced by - resources within the same namespace. - type: object - required: - - spec - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: Desired state of the Issuer resource. - type: object - properties: - acme: - description: |- - ACME configures this issuer to communicate with a RFC8555 (ACME) server - to obtain signed x509 certificates. - type: object - required: - - privateKeySecretRef - - server - properties: - caBundle: - description: |- - Base64-encoded bundle of PEM CAs which can be used to validate the certificate - chain presented by the ACME server. - Mutually exclusive with SkipTLSVerify; prefer using CABundle to prevent various - kinds of security vulnerabilities. - If CABundle and SkipTLSVerify are unset, the system certificate bundle inside - the container is used to validate the TLS connection. - type: string - format: byte - disableAccountKeyGeneration: - description: |- - Enables or disables generating a new ACME account key. - If true, the Issuer resource will *not* request a new account but will expect - the account key to be supplied via an existing secret. - If false, the cert-manager system will generate a new ACME account key - for the Issuer. - Defaults to false. - type: boolean - email: - description: |- - Email is the email address to be associated with the ACME account. - This field is optional, but it is strongly recommended to be set. - It will be used to contact you in case of issues with your account or - certificates, including expiry notification emails. - This field may be updated after the account is initially registered. - type: string - enableDurationFeature: - description: |- - Enables requesting a Not After date on certificates that matches the - duration of the certificate. This is not supported by all ACME servers - like Let's Encrypt. If set to true when the ACME server does not support - it, it will create an error on the Order. - Defaults to false. - type: boolean - externalAccountBinding: - description: |- - ExternalAccountBinding is a reference to a CA external account of the ACME - server. - If set, upon registration cert-manager will attempt to associate the given - external account credentials with the registered ACME account. - type: object - required: - - keyID - - keySecretRef - properties: - keyAlgorithm: - description: |- - Deprecated: keyAlgorithm field exists for historical compatibility - reasons and should not be used. The algorithm is now hardcoded to HS256 - in golang/x/crypto/acme. - type: string - enum: - - HS256 - - HS384 - - HS512 - keyID: - description: keyID is the ID of the CA key that the External Account is bound to. - type: string - keySecretRef: - description: |- - keySecretRef is a Secret Key Selector referencing a data item in a Kubernetes - Secret which holds the symmetric MAC key of the External Account Binding. - The `key` is the index string that is paired with the key data in the - Secret and should not be confused with the key data itself, or indeed with - the External Account Binding keyID above. - The secret key stored in the Secret **must** be un-padded, base64 URL - encoded data. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - preferredChain: - description: |- - PreferredChain is the chain to use if the ACME server outputs multiple. - PreferredChain is no guarantee that this one gets delivered by the ACME - endpoint. - For example, for Let's Encrypt's DST crosssign you would use: - "DST Root CA X3" or "ISRG Root X1" for the newer Let's Encrypt root CA. - This value picks the first certificate bundle in the combined set of - ACME default and alternative chains that has a root-most certificate with - this value as its issuer's commonname. - type: string - maxLength: 64 - privateKeySecretRef: - description: |- - PrivateKey is the name of a Kubernetes Secret resource that will be used to - store the automatically generated ACME account private key. - Optionally, a `key` may be specified to select a specific entry within - the named Secret resource. - If `key` is not specified, a default of `tls.key` will be used. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - server: - description: |- - Server is the URL used to access the ACME server's 'directory' endpoint. - For example, for Let's Encrypt's staging endpoint, you would use: - "https://acme-staging-v02.api.letsencrypt.org/directory". - Only ACME v2 endpoints (i.e. RFC 8555) are supported. - type: string - skipTLSVerify: - description: |- - INSECURE: Enables or disables validation of the ACME server TLS certificate. - If true, requests to the ACME server will not have the TLS certificate chain - validated. - Mutually exclusive with CABundle; prefer using CABundle to prevent various - kinds of security vulnerabilities. - Only enable this option in development environments. - If CABundle and SkipTLSVerify are unset, the system certificate bundle inside - the container is used to validate the TLS connection. - Defaults to false. - type: boolean - solvers: - description: |- - Solvers is a list of challenge solvers that will be used to solve - ACME challenges for the matching domains. - Solver configurations must be provided in order to obtain certificates - from an ACME server. - For more information, see: https://cert-manager.io/docs/configuration/acme/ - type: array - items: - description: |- - An ACMEChallengeSolver describes how to solve ACME challenges for the issuer it is part of. - A selector may be provided to use different solving strategies for different DNS names. - Only one of HTTP01 or DNS01 must be provided. - type: object - properties: - dns01: - description: |- - Configures cert-manager to attempt to complete authorizations by - performing the DNS01 challenge flow. - type: object - properties: - acmeDNS: - description: |- - Use the 'ACME DNS' (https://github.com/joohoi/acme-dns) API to manage - DNS01 challenge records. - type: object - required: - - accountSecretRef - - host - properties: - accountSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - host: - type: string - akamai: - description: Use the Akamai DNS zone management API to manage DNS01 challenge records. - type: object - required: - - accessTokenSecretRef - - clientSecretSecretRef - - clientTokenSecretRef - - serviceConsumerDomain - properties: - accessTokenSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientSecretSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientTokenSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - serviceConsumerDomain: - type: string - azureDNS: - description: Use the Microsoft Azure DNS API to manage DNS01 challenge records. - type: object - required: - - resourceGroupName - - subscriptionID - properties: - clientID: - description: |- - Auth: Azure Service Principal: - The ClientID of the Azure Service Principal used to authenticate with Azure DNS. - If set, ClientSecret and TenantID must also be set. - type: string - clientSecretSecretRef: - description: |- - Auth: Azure Service Principal: - A reference to a Secret containing the password associated with the Service Principal. - If set, ClientID and TenantID must also be set. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - environment: - description: name of the Azure environment (default AzurePublicCloud) - type: string - enum: - - AzurePublicCloud - - AzureChinaCloud - - AzureGermanCloud - - AzureUSGovernmentCloud - hostedZoneName: - description: name of the DNS zone that should be used - type: string - managedIdentity: - description: |- - Auth: Azure Workload Identity or Azure Managed Service Identity: - Settings to enable Azure Workload Identity or Azure Managed Service Identity - If set, ClientID, ClientSecret and TenantID must not be set. - type: object - properties: - clientID: - description: client ID of the managed identity, can not be used at the same time as resourceID - type: string - resourceID: - description: |- - resource ID of the managed identity, can not be used at the same time as clientID - Cannot be used for Azure Managed Service Identity - type: string - tenantID: - description: tenant ID of the managed identity, can not be used at the same time as resourceID - type: string - resourceGroupName: - description: resource group the DNS zone is located in - type: string - subscriptionID: - description: ID of the Azure subscription - type: string - tenantID: - description: |- - Auth: Azure Service Principal: - The TenantID of the Azure Service Principal used to authenticate with Azure DNS. - If set, ClientID and ClientSecret must also be set. - type: string - cloudDNS: - description: Use the Google Cloud DNS API to manage DNS01 challenge records. - type: object - required: - - project - properties: - hostedZoneName: - description: |- - HostedZoneName is an optional field that tells cert-manager in which - Cloud DNS zone the challenge record has to be created. - If left empty cert-manager will automatically choose a zone. - type: string - project: - type: string - serviceAccountSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - cloudflare: - description: Use the Cloudflare API to manage DNS01 challenge records. - type: object - properties: - apiKeySecretRef: - description: |- - API key to use to authenticate with Cloudflare. - Note: using an API token to authenticate is now the recommended method - as it allows greater control of permissions. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - apiTokenSecretRef: - description: API token used to authenticate with Cloudflare. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - email: - description: Email of the account, only required when using API key based authentication. - type: string - cnameStrategy: - description: |- - CNAMEStrategy configures how the DNS01 provider should handle CNAME - records when found in DNS zones. - type: string - enum: - - None - - Follow - digitalocean: - description: Use the DigitalOcean DNS API to manage DNS01 challenge records. - type: object - required: - - tokenSecretRef - properties: - tokenSecretRef: - description: |- - A reference to a specific 'key' within a Secret resource. - In some instances, `key` is a required field. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - rfc2136: - description: |- - Use RFC2136 ("Dynamic Updates in the Domain Name System") (https://datatracker.ietf.org/doc/rfc2136/) - to manage DNS01 challenge records. - type: object - required: - - nameserver - properties: - nameserver: - description: |- - The IP address or hostname of an authoritative DNS server supporting - RFC2136 in the form host:port. If the host is an IPv6 address it must be - enclosed in square brackets (e.g [2001:db8::1]) ; port is optional. - This field is required. - type: string - tsigAlgorithm: - description: |- - The TSIG Algorithm configured in the DNS supporting RFC2136. Used only - when ``tsigSecretSecretRef`` and ``tsigKeyName`` are defined. - Supported values are (case-insensitive): ``HMACMD5`` (default), - ``HMACSHA1``, ``HMACSHA256`` or ``HMACSHA512``. - type: string - tsigKeyName: - description: |- - The TSIG Key name configured in the DNS. - If ``tsigSecretSecretRef`` is defined, this field is required. - type: string - tsigSecretSecretRef: - description: |- - The name of the secret containing the TSIG value. - If ``tsigKeyName`` is defined, this field is required. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - route53: - description: Use the AWS Route53 API to manage DNS01 challenge records. - type: object - properties: - accessKeyID: - description: |- - The AccessKeyID is used for authentication. - Cannot be set when SecretAccessKeyID is set. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, - see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials - type: string - accessKeyIDSecretRef: - description: |- - The SecretAccessKey is used for authentication. If set, pull the AWS - access key ID from a key within a Kubernetes Secret. - Cannot be set when AccessKeyID is set. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, - see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - auth: - description: Auth configures how cert-manager authenticates. - type: object - required: - - kubernetes - properties: - kubernetes: - description: |- - Kubernetes authenticates with Route53 using AssumeRoleWithWebIdentity - by passing a bound ServiceAccount token. - type: object - required: - - serviceAccountRef - properties: - serviceAccountRef: - description: |- - A reference to a service account that will be used to request a bound - token (also known as "projected token"). To use this field, you must - configure an RBAC rule to let cert-manager request a token. - type: object - required: - - name - properties: - audiences: - description: |- - TokenAudiences is an optional list of audiences to include in the - token passed to AWS. The default token consisting of the issuer's namespace - and name is always included. - If unset the audience defaults to `sts.amazonaws.com`. - type: array - items: - type: string - name: - description: Name of the ServiceAccount used to request a token. - type: string - hostedZoneID: - description: If set, the provider will manage only this zone in Route53 and will not do a lookup using the route53:ListHostedZonesByName api call. - type: string - region: - description: |- - Override the AWS region. - - Route53 is a global service and does not have regional endpoints but the - region specified here (or via environment variables) is used as a hint to - help compute the correct AWS credential scope and partition when it - connects to Route53. See: - - [Amazon Route 53 endpoints and quotas](https://docs.aws.amazon.com/general/latest/gr/r53.html) - - [Global services](https://docs.aws.amazon.com/whitepapers/latest/aws-fault-isolation-boundaries/global-services.html) - - If you omit this region field, cert-manager will use the region from - AWS_REGION and AWS_DEFAULT_REGION environment variables, if they are set - in the cert-manager controller Pod. - - The `region` field is not needed if you use [IAM Roles for Service Accounts (IRSA)](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html). - Instead an AWS_REGION environment variable is added to the cert-manager controller Pod by: - [Amazon EKS Pod Identity Webhook](https://github.com/aws/amazon-eks-pod-identity-webhook). - In this case this `region` field value is ignored. - - The `region` field is not needed if you use [EKS Pod Identities](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html). - Instead an AWS_REGION environment variable is added to the cert-manager controller Pod by: - [Amazon EKS Pod Identity Agent](https://github.com/aws/eks-pod-identity-agent), - In this case this `region` field value is ignored. - type: string - role: - description: |- - Role is a Role ARN which the Route53 provider will assume using either the explicit credentials AccessKeyID/SecretAccessKey - or the inferred credentials from environment variables, shared credentials file or AWS Instance metadata - type: string - secretAccessKeySecretRef: - description: |- - The SecretAccessKey is used for authentication. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, - see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - webhook: - description: |- - Configure an external webhook based DNS01 challenge solver to manage - DNS01 challenge records. - type: object - required: - - groupName - - solverName - properties: - config: - description: |- - Additional configuration that should be passed to the webhook apiserver - when challenges are processed. - This can contain arbitrary JSON data. - Secret values should not be specified in this stanza. - If secret values are needed (e.g. credentials for a DNS service), you - should use a SecretKeySelector to reference a Secret resource. - For details on the schema of this field, consult the webhook provider - implementation's documentation. - x-kubernetes-preserve-unknown-fields: true - groupName: - description: |- - The API group name that should be used when POSTing ChallengePayload - resources to the webhook apiserver. - This should be the same as the GroupName specified in the webhook - provider implementation. - type: string - solverName: - description: |- - The name of the solver to use, as defined in the webhook provider - implementation. - This will typically be the name of the provider, e.g. 'cloudflare'. - type: string - http01: - description: |- - Configures cert-manager to attempt to complete authorizations by - performing the HTTP01 challenge flow. - It is not possible to obtain certificates for wildcard domain names - (e.g. `*.example.com`) using the HTTP01 challenge mechanism. - type: object - properties: - gatewayHTTPRoute: - description: |- - The Gateway API is a sig-network community API that models service networking - in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will - create HTTPRoutes with the specified labels in the same namespace as the challenge. - This solver is experimental, and fields / behaviour may change in the future. - type: object - properties: - labels: - description: |- - Custom labels that will be applied to HTTPRoutes created by cert-manager - while solving HTTP-01 challenges. - type: object - additionalProperties: - type: string - parentRefs: - description: |- - When solving an HTTP-01 challenge, cert-manager creates an HTTPRoute. - cert-manager needs to know which parentRefs should be used when creating - the HTTPRoute. Usually, the parentRef references a Gateway. See: - https://gateway-api.sigs.k8s.io/api-types/httproute/#attaching-to-gateways - type: array - items: - description: |- - ParentReference identifies an API object (usually a Gateway) that can be considered - a parent of this resource (usually a route). There are two kinds of parent resources - with "Core" support: - - * Gateway (Gateway conformance profile) - * Service (Mesh conformance profile, ClusterIP Services only) - - This API may be extended in the future to support additional kinds of parent - resources. - - The API object must be valid in the cluster; the Group and Kind must - be registered in the cluster for this reference to be valid. - type: object - required: - - name - properties: - group: - description: |- - Group is the group of the referent. - When unspecified, "gateway.networking.k8s.io" is inferred. - To set the core API group (such as for a "Service" kind referent), - Group must be explicitly set to "" (empty string). - - Support: Core - type: string - default: gateway.networking.k8s.io - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - kind: - description: |- - Kind is kind of the referent. - - There are two kinds of parent resources with "Core" support: - - * Gateway (Gateway conformance profile) - * Service (Mesh conformance profile, ClusterIP Services only) - - Support for other resources is Implementation-Specific. - type: string - default: Gateway - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - name: - description: |- - Name is the name of the referent. - - Support: Core - type: string - maxLength: 253 - minLength: 1 - namespace: - description: |- - Namespace is the namespace of the referent. When unspecified, this refers - to the local namespace of the Route. - - Note that there are specific rules for ParentRefs which cross namespace - boundaries. Cross-namespace references are only valid if they are explicitly - allowed by something in the namespace they are referring to. For example: - Gateway has the AllowedRoutes field, and ReferenceGrant provides a - generic way to enable any other kind of cross-namespace reference. - - - ParentRefs from a Route to a Service in the same namespace are "producer" - routes, which apply default routing rules to inbound connections from - any namespace to the Service. - - ParentRefs from a Route to a Service in a different namespace are - "consumer" routes, and these routing rules are only applied to outbound - connections originating from the same namespace as the Route, for which - the intended destination of the connections are a Service targeted as a - ParentRef of the Route. - - - Support: Core - type: string - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - port: - description: |- - Port is the network port this Route targets. It can be interpreted - differently based on the type of parent resource. - - When the parent resource is a Gateway, this targets all listeners - listening on the specified port that also support this kind of Route(and - select this Route). It's not recommended to set `Port` unless the - networking behaviors specified in a Route must apply to a specific port - as opposed to a listener(s) whose port(s) may be changed. When both Port - and SectionName are specified, the name and port of the selected listener - must match both specified values. - - - When the parent resource is a Service, this targets a specific port in the - Service spec. When both Port (experimental) and SectionName are specified, - the name and port of the selected port must match both specified values. - - - Implementations MAY choose to support other parent resources. - Implementations supporting other types of parent resources MUST clearly - document how/if Port is interpreted. - - For the purpose of status, an attachment is considered successful as - long as the parent resource accepts it partially. For example, Gateway - listeners can restrict which Routes can attach to them by Route kind, - namespace, or hostname. If 1 of 2 Gateway listeners accept attachment - from the referencing Route, the Route MUST be considered successfully - attached. If no Gateway listeners accept attachment from this Route, - the Route MUST be considered detached from the Gateway. - - Support: Extended - type: integer - format: int32 - maximum: 65535 - minimum: 1 - sectionName: - description: |- - SectionName is the name of a section within the target resource. In the - following resources, SectionName is interpreted as the following: - - * Gateway: Listener name. When both Port (experimental) and SectionName - are specified, the name and port of the selected listener must match - both specified values. - * Service: Port name. When both Port (experimental) and SectionName - are specified, the name and port of the selected listener must match - both specified values. - - Implementations MAY choose to support attaching Routes to other resources. - If that is the case, they MUST clearly document how SectionName is - interpreted. - - When unspecified (empty string), this will reference the entire resource. - For the purpose of status, an attachment is considered successful if at - least one section in the parent resource accepts it. For example, Gateway - listeners can restrict which Routes can attach to them by Route kind, - namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from - the referencing Route, the Route MUST be considered successfully - attached. If no Gateway listeners accept attachment from this Route, the - Route MUST be considered detached from the Gateway. - - Support: Core - type: string - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - podTemplate: - description: |- - Optional pod template used to configure the ACME challenge solver pods - used for HTTP01 challenges. - type: object - properties: - metadata: - description: |- - ObjectMeta overrides for the pod used to solve HTTP01 challenges. - Only the 'labels' and 'annotations' fields may be set. - If labels or annotations overlap with in-built values, the values here - will override the in-built values. - type: object - properties: - annotations: - description: Annotations that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - labels: - description: Labels that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - spec: - description: |- - PodSpec defines overrides for the HTTP01 challenge solver pod. - Check ACMEChallengeSolverHTTP01IngressPodSpec to find out currently supported fields. - All other fields will be ignored. - type: object - properties: - affinity: - description: If specified, the pod's scheduling constraints - type: object - properties: - nodeAffinity: - description: Describes node affinity scheduling rules for the pod. - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node matches the corresponding matchExpressions; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: |- - An empty preferred scheduling term matches all objects with implicit weight 0 - (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). - type: object - required: - - preference - - weight - properties: - preference: - description: A node selector term, associated with the corresponding weight. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - weight: - description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to an update), the system - may or may not try to eventually evict the pod from its node. - type: object - required: - - nodeSelectorTerms - properties: - nodeSelectorTerms: - description: Required. A list of node selector terms. The terms are ORed. - type: array - items: - description: |- - A null or empty node selector term matches no objects. The requirements of - them are ANDed. - The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - podAffinity: - description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - podAntiAffinity: - description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the anti-affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the anti-affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the anti-affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - imagePullSecrets: - description: If specified, the pod's imagePullSecrets - type: array - items: - description: |- - LocalObjectReference contains enough information to let you locate the - referenced object inside the same namespace. - type: object - properties: - name: - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - default: "" - x-kubernetes-map-type: atomic - nodeSelector: - description: |- - NodeSelector is a selector which must be true for the pod to fit on a node. - Selector which must match a node's labels for the pod to be scheduled on that node. - More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ - type: object - additionalProperties: - type: string - priorityClassName: - description: If specified, the pod's priorityClassName. - type: string - securityContext: - description: If specified, the pod's security context - type: object - properties: - fsGroup: - description: |- - A special supplemental group that applies to all containers in a pod. - Some volume types allow the Kubelet to change the ownership of that volume - to be owned by the pod: - - 1. The owning GID will be the FSGroup - 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) - 3. The permission bits are OR'd with rw-rw---- - - If unset, the Kubelet will not modify the ownership and permissions of any volume. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - fsGroupChangePolicy: - description: |- - fsGroupChangePolicy defines behavior of changing ownership and permission of the volume - before being exposed inside Pod. This field will only apply to - volume types which support fsGroup based ownership(and permissions). - It will have no effect on ephemeral volume types such as: secret, configmaps - and emptydir. - Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. - Note that this field cannot be set when spec.os.name is windows. - type: string - runAsGroup: - description: |- - The GID to run the entrypoint of the container process. - Uses runtime default if unset. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - runAsNonRoot: - description: |- - Indicates that the container must run as a non-root user. - If true, the Kubelet will validate the image at runtime to ensure that it - does not run as UID 0 (root) and fail to start the container if it does. - If unset or false, no such validation will be performed. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: |- - The UID to run the entrypoint of the container process. - Defaults to user specified in image metadata if unspecified. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - seLinuxOptions: - description: |- - The SELinux context to be applied to all containers. - If unspecified, the container runtime will allocate a random SELinux context for each - container. May also be set in SecurityContext. If set in - both SecurityContext and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. - Note that this field cannot be set when spec.os.name is windows. - type: object - properties: - level: - description: Level is SELinux level label that applies to the container. - type: string - role: - description: Role is a SELinux role label that applies to the container. - type: string - type: - description: Type is a SELinux type label that applies to the container. - type: string - user: - description: User is a SELinux user label that applies to the container. - type: string - seccompProfile: - description: |- - The seccomp options to use by the containers in this pod. - Note that this field cannot be set when spec.os.name is windows. - type: object - required: - - type - properties: - localhostProfile: - description: |- - localhostProfile indicates a profile defined in a file on the node should be used. - The profile must be preconfigured on the node to work. - Must be a descending path, relative to the kubelet's configured seccomp profile location. - Must be set if type is "Localhost". Must NOT be set for any other type. - type: string - type: - description: |- - type indicates which kind of seccomp profile will be applied. - Valid options are: - - Localhost - a profile defined in a file on the node should be used. - RuntimeDefault - the container runtime default profile should be used. - Unconfined - no profile should be applied. - type: string - supplementalGroups: - description: |- - A list of groups applied to the first process run in each container, in addition - to the container's primary GID, the fsGroup (if specified), and group memberships - defined in the container image for the uid of the container process. If unspecified, - no additional groups are added to any container. Note that group memberships - defined in the container image for the uid of the container process are still effective, - even if they are not included in this list. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - type: integer - format: int64 - sysctls: - description: |- - Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported - sysctls (by the container runtime) might fail to launch. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - description: Sysctl defines a kernel parameter to be set - type: object - required: - - name - - value - properties: - name: - description: Name of a property to set - type: string - value: - description: Value of a property to set - type: string - serviceAccountName: - description: If specified, the pod's service account - type: string - tolerations: - description: If specified, the pod's tolerations. - type: array - items: - description: |- - The pod this Toleration is attached to tolerates any taint that matches - the triple using the matching operator . - type: object - properties: - effect: - description: |- - Effect indicates the taint effect to match. Empty means match all taint effects. - When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: |- - Key is the taint key that the toleration applies to. Empty means match all taint keys. - If the key is empty, operator must be Exists; this combination means to match all values and all keys. - type: string - operator: - description: |- - Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. - Exists is equivalent to wildcard for value, so that a pod can - tolerate all taints of a particular category. - type: string - tolerationSeconds: - description: |- - TolerationSeconds represents the period of time the toleration (which must be - of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, - it is not set, which means tolerate the taint forever (do not evict). Zero and - negative values will be treated as 0 (evict immediately) by the system. - type: integer - format: int64 - value: - description: |- - Value is the taint value the toleration matches to. - If the operator is Exists, the value should be empty, otherwise just a regular string. - type: string - serviceType: - description: |- - Optional service type for Kubernetes solver service. Supported values - are NodePort or ClusterIP. If unset, defaults to NodePort. - type: string - ingress: - description: |- - The ingress based HTTP01 challenge solver will solve challenges by - creating or modifying Ingress resources in order to route requests for - '/.well-known/acme-challenge/XYZ' to 'challenge solver' pods that are - provisioned by cert-manager for each Challenge to be completed. - type: object - properties: - class: - description: |- - This field configures the annotation `kubernetes.io/ingress.class` when - creating Ingress resources to solve ACME challenges that use this - challenge solver. Only one of `class`, `name` or `ingressClassName` may - be specified. - type: string - ingressClassName: - description: |- - This field configures the field `ingressClassName` on the created Ingress - resources used to solve ACME challenges that use this challenge solver. - This is the recommended way of configuring the ingress class. Only one of - `class`, `name` or `ingressClassName` may be specified. - type: string - ingressTemplate: - description: |- - Optional ingress template used to configure the ACME challenge solver - ingress used for HTTP01 challenges. - type: object - properties: - metadata: - description: |- - ObjectMeta overrides for the ingress used to solve HTTP01 challenges. - Only the 'labels' and 'annotations' fields may be set. - If labels or annotations overlap with in-built values, the values here - will override the in-built values. - type: object - properties: - annotations: - description: Annotations that should be added to the created ACME HTTP01 solver ingress. - type: object - additionalProperties: - type: string - labels: - description: Labels that should be added to the created ACME HTTP01 solver ingress. - type: object - additionalProperties: - type: string - name: - description: |- - The name of the ingress resource that should have ACME challenge solving - routes inserted into it in order to solve HTTP01 challenges. - This is typically used in conjunction with ingress controllers like - ingress-gce, which maintains a 1:1 mapping between external IPs and - ingress resources. Only one of `class`, `name` or `ingressClassName` may - be specified. - type: string - podTemplate: - description: |- - Optional pod template used to configure the ACME challenge solver pods - used for HTTP01 challenges. - type: object - properties: - metadata: - description: |- - ObjectMeta overrides for the pod used to solve HTTP01 challenges. - Only the 'labels' and 'annotations' fields may be set. - If labels or annotations overlap with in-built values, the values here - will override the in-built values. - type: object - properties: - annotations: - description: Annotations that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - labels: - description: Labels that should be added to the created ACME HTTP01 solver pods. - type: object - additionalProperties: - type: string - spec: - description: |- - PodSpec defines overrides for the HTTP01 challenge solver pod. - Check ACMEChallengeSolverHTTP01IngressPodSpec to find out currently supported fields. - All other fields will be ignored. - type: object - properties: - affinity: - description: If specified, the pod's scheduling constraints - type: object - properties: - nodeAffinity: - description: Describes node affinity scheduling rules for the pod. - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node matches the corresponding matchExpressions; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: |- - An empty preferred scheduling term matches all objects with implicit weight 0 - (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). - type: object - required: - - preference - - weight - properties: - preference: - description: A node selector term, associated with the corresponding weight. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - weight: - description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to an update), the system - may or may not try to eventually evict the pod from its node. - type: object - required: - - nodeSelectorTerms - properties: - nodeSelectorTerms: - description: Required. A list of node selector terms. The terms are ORed. - type: array - items: - description: |- - A null or empty node selector term matches no objects. The requirements of - them are ANDed. - The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. - type: object - properties: - matchExpressions: - description: A list of node selector requirements by node's labels. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements by node's fields. - type: array - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - x-kubernetes-list-type: atomic - x-kubernetes-map-type: atomic - podAffinity: - description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - podAntiAffinity: - description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). - type: object - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the anti-affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - type: array - items: - description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) - type: object - required: - - podAffinityTerm - - weight - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated with the corresponding weight. - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - type: integer - format: int32 - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the anti-affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the anti-affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - type: array - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - type: object - required: - - topologyKey - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). - type: array - items: - type: string - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - type: array - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - type: array - items: - type: string - x-kubernetes-list-type: atomic - x-kubernetes-list-type: atomic - matchLabels: - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - additionalProperties: - type: string - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - type: array - items: - type: string - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - x-kubernetes-list-type: atomic - imagePullSecrets: - description: If specified, the pod's imagePullSecrets - type: array - items: - description: |- - LocalObjectReference contains enough information to let you locate the - referenced object inside the same namespace. - type: object - properties: - name: - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - default: "" - x-kubernetes-map-type: atomic - nodeSelector: - description: |- - NodeSelector is a selector which must be true for the pod to fit on a node. - Selector which must match a node's labels for the pod to be scheduled on that node. - More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ - type: object - additionalProperties: - type: string - priorityClassName: - description: If specified, the pod's priorityClassName. - type: string - securityContext: - description: If specified, the pod's security context - type: object - properties: - fsGroup: - description: |- - A special supplemental group that applies to all containers in a pod. - Some volume types allow the Kubelet to change the ownership of that volume - to be owned by the pod: - - 1. The owning GID will be the FSGroup - 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) - 3. The permission bits are OR'd with rw-rw---- - - If unset, the Kubelet will not modify the ownership and permissions of any volume. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - fsGroupChangePolicy: - description: |- - fsGroupChangePolicy defines behavior of changing ownership and permission of the volume - before being exposed inside Pod. This field will only apply to - volume types which support fsGroup based ownership(and permissions). - It will have no effect on ephemeral volume types such as: secret, configmaps - and emptydir. - Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. - Note that this field cannot be set when spec.os.name is windows. - type: string - runAsGroup: - description: |- - The GID to run the entrypoint of the container process. - Uses runtime default if unset. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - runAsNonRoot: - description: |- - Indicates that the container must run as a non-root user. - If true, the Kubelet will validate the image at runtime to ensure that it - does not run as UID 0 (root) and fail to start the container if it does. - If unset or false, no such validation will be performed. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: |- - The UID to run the entrypoint of the container process. - Defaults to user specified in image metadata if unspecified. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - type: integer - format: int64 - seLinuxOptions: - description: |- - The SELinux context to be applied to all containers. - If unspecified, the container runtime will allocate a random SELinux context for each - container. May also be set in SecurityContext. If set in - both SecurityContext and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. - Note that this field cannot be set when spec.os.name is windows. - type: object - properties: - level: - description: Level is SELinux level label that applies to the container. - type: string - role: - description: Role is a SELinux role label that applies to the container. - type: string - type: - description: Type is a SELinux type label that applies to the container. - type: string - user: - description: User is a SELinux user label that applies to the container. - type: string - seccompProfile: - description: |- - The seccomp options to use by the containers in this pod. - Note that this field cannot be set when spec.os.name is windows. - type: object - required: - - type - properties: - localhostProfile: - description: |- - localhostProfile indicates a profile defined in a file on the node should be used. - The profile must be preconfigured on the node to work. - Must be a descending path, relative to the kubelet's configured seccomp profile location. - Must be set if type is "Localhost". Must NOT be set for any other type. - type: string - type: - description: |- - type indicates which kind of seccomp profile will be applied. - Valid options are: - - Localhost - a profile defined in a file on the node should be used. - RuntimeDefault - the container runtime default profile should be used. - Unconfined - no profile should be applied. - type: string - supplementalGroups: - description: |- - A list of groups applied to the first process run in each container, in addition - to the container's primary GID, the fsGroup (if specified), and group memberships - defined in the container image for the uid of the container process. If unspecified, - no additional groups are added to any container. Note that group memberships - defined in the container image for the uid of the container process are still effective, - even if they are not included in this list. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - type: integer - format: int64 - sysctls: - description: |- - Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported - sysctls (by the container runtime) might fail to launch. - Note that this field cannot be set when spec.os.name is windows. - type: array - items: - description: Sysctl defines a kernel parameter to be set - type: object - required: - - name - - value - properties: - name: - description: Name of a property to set - type: string - value: - description: Value of a property to set - type: string - serviceAccountName: - description: If specified, the pod's service account - type: string - tolerations: - description: If specified, the pod's tolerations. - type: array - items: - description: |- - The pod this Toleration is attached to tolerates any taint that matches - the triple using the matching operator . - type: object - properties: - effect: - description: |- - Effect indicates the taint effect to match. Empty means match all taint effects. - When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: |- - Key is the taint key that the toleration applies to. Empty means match all taint keys. - If the key is empty, operator must be Exists; this combination means to match all values and all keys. - type: string - operator: - description: |- - Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. - Exists is equivalent to wildcard for value, so that a pod can - tolerate all taints of a particular category. - type: string - tolerationSeconds: - description: |- - TolerationSeconds represents the period of time the toleration (which must be - of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, - it is not set, which means tolerate the taint forever (do not evict). Zero and - negative values will be treated as 0 (evict immediately) by the system. - type: integer - format: int64 - value: - description: |- - Value is the taint value the toleration matches to. - If the operator is Exists, the value should be empty, otherwise just a regular string. - type: string - serviceType: - description: |- - Optional service type for Kubernetes solver service. Supported values - are NodePort or ClusterIP. If unset, defaults to NodePort. - type: string - selector: - description: |- - Selector selects a set of DNSNames on the Certificate resource that - should be solved using this challenge solver. - If not specified, the solver will be treated as the 'default' solver - with the lowest priority, i.e. if any other solver has a more specific - match, it will be used instead. - type: object - properties: - dnsNames: - description: |- - List of DNSNames that this solver will be used to solve. - If specified and a match is found, a dnsNames selector will take - precedence over a dnsZones selector. - If multiple solvers match with the same dnsNames value, the solver - with the most matching labels in matchLabels will be selected. - If neither has more matches, the solver defined earlier in the list - will be selected. - type: array - items: - type: string - dnsZones: - description: |- - List of DNSZones that this solver will be used to solve. - The most specific DNS zone match specified here will take precedence - over other DNS zone matches, so a solver specifying sys.example.com - will be selected over one specifying example.com for the domain - www.sys.example.com. - If multiple solvers match with the same dnsZones value, the solver - with the most matching labels in matchLabels will be selected. - If neither has more matches, the solver defined earlier in the list - will be selected. - type: array - items: - type: string - matchLabels: - description: |- - A label selector that is used to refine the set of certificate's that - this challenge solver will apply to. - type: object - additionalProperties: - type: string - ca: - description: |- - CA configures this issuer to sign certificates using a signing CA keypair - stored in a Secret resource. - This is used to build internal PKIs that are managed by cert-manager. - type: object - required: - - secretName - properties: - crlDistributionPoints: - description: |- - The CRL distribution points is an X.509 v3 certificate extension which identifies - the location of the CRL from which the revocation of this certificate can be checked. - If not set, certificates will be issued without distribution points set. - type: array - items: - type: string - issuingCertificateURLs: - description: |- - IssuingCertificateURLs is a list of URLs which this issuer should embed into certificates - it creates. See https://www.rfc-editor.org/rfc/rfc5280#section-4.2.2.1 for more details. - As an example, such a URL might be "http://ca.domain.com/ca.crt". - type: array - items: - type: string - ocspServers: - description: |- - The OCSP server list is an X.509 v3 extension that defines a list of - URLs of OCSP responders. The OCSP responders can be queried for the - revocation status of an issued certificate. If not set, the - certificate will be issued with no OCSP servers set. For example, an - OCSP server URL could be "http://ocsp.int-x3.letsencrypt.org". - type: array - items: - type: string - secretName: - description: |- - SecretName is the name of the secret used to sign Certificates issued - by this Issuer. - type: string - selfSigned: - description: |- - SelfSigned configures this issuer to 'self sign' certificates using the - private key used to create the CertificateRequest object. - type: object - properties: - crlDistributionPoints: - description: |- - The CRL distribution points is an X.509 v3 certificate extension which identifies - the location of the CRL from which the revocation of this certificate can be checked. - If not set certificate will be issued without CDP. Values are strings. - type: array - items: - type: string - vault: - description: |- - Vault configures this issuer to sign certificates using a HashiCorp Vault - PKI backend. - type: object - required: - - auth - - path - - server - properties: - auth: - description: Auth configures how cert-manager authenticates with the Vault server. - type: object - properties: - appRole: - description: |- - AppRole authenticates with Vault using the App Role auth mechanism, - with the role and secret stored in a Kubernetes Secret resource. - type: object - required: - - path - - roleId - - secretRef - properties: - path: - description: |- - Path where the App Role authentication backend is mounted in Vault, e.g: - "approle" - type: string - roleId: - description: |- - RoleID configured in the App Role authentication backend when setting - up the authentication backend in Vault. - type: string - secretRef: - description: |- - Reference to a key in a Secret that contains the App Role secret used - to authenticate with Vault. - The `key` field must be specified and denotes which entry within the Secret - resource is used as the app role secret. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientCertificate: - description: |- - ClientCertificate authenticates with Vault by presenting a client - certificate during the request's TLS handshake. - Works only when using HTTPS protocol. - type: object - properties: - mountPath: - description: |- - The Vault mountPath here is the mount path to use when authenticating with - Vault. For example, setting a value to `/v1/auth/foo`, will use the path - `/v1/auth/foo/login` to authenticate with Vault. If unspecified, the - default value "/v1/auth/cert" will be used. - type: string - name: - description: |- - Name of the certificate role to authenticate against. - If not set, matching any certificate role, if available. - type: string - secretName: - description: |- - Reference to Kubernetes Secret of type "kubernetes.io/tls" (hence containing - tls.crt and tls.key) used to authenticate to Vault using TLS client - authentication. - type: string - kubernetes: - description: |- - Kubernetes authenticates with Vault by passing the ServiceAccount - token stored in the named Secret resource to the Vault server. - type: object - required: - - role - properties: - mountPath: - description: |- - The Vault mountPath here is the mount path to use when authenticating with - Vault. For example, setting a value to `/v1/auth/foo`, will use the path - `/v1/auth/foo/login` to authenticate with Vault. If unspecified, the - default value "/v1/auth/kubernetes" will be used. - type: string - role: - description: |- - A required field containing the Vault Role to assume. A Role binds a - Kubernetes ServiceAccount with a set of Vault policies. - type: string - secretRef: - description: |- - The required Secret field containing a Kubernetes ServiceAccount JWT used - for authenticating with Vault. Use of 'ambient credentials' is not - supported. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - serviceAccountRef: - description: |- - A reference to a service account that will be used to request a bound - token (also known as "projected token"). Compared to using "secretRef", - using this field means that you don't rely on statically bound tokens. To - use this field, you must configure an RBAC rule to let cert-manager - request a token. - type: object - required: - - name - properties: - audiences: - description: |- - TokenAudiences is an optional list of extra audiences to include in the token passed to Vault. The default token - consisting of the issuer's namespace and name is always included. - type: array - items: - type: string - name: - description: Name of the ServiceAccount used to request a token. - type: string - tokenSecretRef: - description: TokenSecretRef authenticates with Vault by presenting a token. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - caBundle: - description: |- - Base64-encoded bundle of PEM CAs which will be used to validate the certificate - chain presented by Vault. Only used if using HTTPS to connect to Vault and - ignored for HTTP connections. - Mutually exclusive with CABundleSecretRef. - If neither CABundle nor CABundleSecretRef are defined, the certificate bundle in - the cert-manager controller container is used to validate the TLS connection. - type: string - format: byte - caBundleSecretRef: - description: |- - Reference to a Secret containing a bundle of PEM-encoded CAs to use when - verifying the certificate chain presented by Vault when using HTTPS. - Mutually exclusive with CABundle. - If neither CABundle nor CABundleSecretRef are defined, the certificate bundle in - the cert-manager controller container is used to validate the TLS connection. - If no key for the Secret is specified, cert-manager will default to 'ca.crt'. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientCertSecretRef: - description: |- - Reference to a Secret containing a PEM-encoded Client Certificate to use when the - Vault server requires mTLS. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - clientKeySecretRef: - description: |- - Reference to a Secret containing a PEM-encoded Client Private Key to use when the - Vault server requires mTLS. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - namespace: - description: |- - Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1" - More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces - type: string - path: - description: |- - Path is the mount path of the Vault PKI backend's `sign` endpoint, e.g: - "my_pki_mount/sign/my-role-name". - type: string - server: - description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' - type: string - venafi: - description: |- - Venafi configures this issuer to sign certificates using a Venafi TPP - or Venafi Cloud policy zone. - type: object - required: - - zone - properties: - cloud: - description: |- - Cloud specifies the Venafi cloud configuration settings. - Only one of TPP or Cloud may be specified. - type: object - required: - - apiTokenSecretRef - properties: - apiTokenSecretRef: - description: APITokenSecretRef is a secret key selector for the Venafi Cloud API token. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - url: - description: |- - URL is the base URL for Venafi Cloud. - Defaults to "https://api.venafi.cloud/v1". - type: string - tpp: - description: |- - TPP specifies Trust Protection Platform configuration settings. - Only one of TPP or Cloud may be specified. - type: object - required: - - credentialsRef - - url - properties: - caBundle: - description: |- - Base64-encoded bundle of PEM CAs which will be used to validate the certificate - chain presented by the TPP server. Only used if using HTTPS; ignored for HTTP. - If undefined, the certificate bundle in the cert-manager controller container - is used to validate the chain. - type: string - format: byte - caBundleSecretRef: - description: |- - Reference to a Secret containing a base64-encoded bundle of PEM CAs - which will be used to validate the certificate chain presented by the TPP server. - Only used if using HTTPS; ignored for HTTP. Mutually exclusive with CABundle. - If neither CABundle nor CABundleSecretRef is defined, the certificate bundle in - the cert-manager controller container is used to validate the TLS connection. - type: object - required: - - name - properties: - key: - description: |- - The key of the entry in the Secret resource's `data` field to be used. - Some instances of this field may be defaulted, in others it may be - required. - type: string - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - credentialsRef: - description: |- - CredentialsRef is a reference to a Secret containing the Venafi TPP API credentials. - The secret must contain the key 'access-token' for the Access Token Authentication, - or two keys, 'username' and 'password' for the API Keys Authentication. - type: object - required: - - name - properties: - name: - description: |- - Name of the resource being referred to. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - url: - description: |- - URL is the base URL for the vedsdk endpoint of the Venafi TPP instance, - for example: "https://tpp.example.com/vedsdk". - type: string - zone: - description: |- - Zone is the Venafi Policy Zone to use for this issuer. - All requests made to the Venafi platform will be restricted by the named - zone policy. - This field is required. - type: string - status: - description: Status of the Issuer. This is set and managed automatically. - type: object - properties: - acme: - description: |- - ACME specific status options. - This field should only be set if the Issuer is configured to use an ACME - server to issue certificates. - type: object - properties: - lastPrivateKeyHash: - description: |- - LastPrivateKeyHash is a hash of the private key associated with the latest - registered ACME account, in order to track changes made to registered account - associated with the Issuer - type: string - lastRegisteredEmail: - description: |- - LastRegisteredEmail is the email associated with the latest registered - ACME account, in order to track changes made to registered account - associated with the Issuer - type: string - uri: - description: |- - URI is the unique account identifier, which can also be used to retrieve - account details from the CA - type: string - conditions: - description: |- - List of status conditions to indicate the status of a CertificateRequest. - Known condition types are `Ready`. - type: array - items: - description: IssuerCondition contains condition information for an Issuer. - type: object - required: - - status - - type - properties: - lastTransitionTime: - description: |- - LastTransitionTime is the timestamp corresponding to the last status - change of this condition. - type: string - format: date-time - message: - description: |- - Message is a human readable description of the details of the last - transition, complementing reason. - type: string - observedGeneration: - description: |- - If set, this represents the .metadata.generation that the condition was - set based upon. - For instance, if .metadata.generation is currently 12, but the - .status.condition[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the Issuer. - type: integer - format: int64 - reason: - description: |- - Reason is a brief machine readable explanation for the condition's last - transition. - type: string - status: - description: Status of the condition, one of (`True`, `False`, `Unknown`). - type: string - enum: - - "True" - - "False" - - Unknown - type: - description: Type of the condition, known values are (`Ready`). - type: string - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - served: true - storage: true - -# END crd {{- end }} - ---- -# START crd {{- if or .Values.crds.enabled .Values.installCRDs }} -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: orders.acme.cert-manager.io - # START annotations {{- if .Values.crds.keep }} - annotations: - helm.sh/resource-policy: keep - # END annotations {{- end }} - labels: - app: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/name: '{{ template "cert-manager.name" . }}' - app.kubernetes.io/instance: '{{ .Release.Name }}' - app.kubernetes.io/component: "crds" - # Generated labels {{- include "labels" . | nindent 4 }} -spec: - group: acme.cert-manager.io - names: - kind: Order - listKind: OrderList - plural: orders - singular: order - categories: - - cert-manager - - cert-manager-acme - scope: Namespaced - versions: - - name: v1 - subresources: - status: {} - additionalPrinterColumns: - - jsonPath: .status.state - name: State - type: string - - jsonPath: .spec.issuerRef.name - name: Issuer - priority: 1 - type: string - - jsonPath: .status.reason - name: Reason - priority: 1 - type: string - - jsonPath: .metadata.creationTimestamp - description: CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC. - name: Age - type: date - schema: - openAPIV3Schema: - description: Order is a type to represent an Order with an ACME server - type: object - required: - - metadata - - spec - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - type: object - required: - - issuerRef - - request - properties: - commonName: - description: |- - CommonName is the common name as specified on the DER encoded CSR. - If specified, this value must also be present in `dnsNames` or `ipAddresses`. - This field must match the corresponding field on the DER encoded CSR. - type: string - dnsNames: - description: |- - DNSNames is a list of DNS names that should be included as part of the Order - validation process. - This field must match the corresponding field on the DER encoded CSR. - type: array - items: - type: string - duration: - description: |- - Duration is the duration for the not after date for the requested certificate. - this is set on order creation as pe the ACME spec. - type: string - ipAddresses: - description: |- - IPAddresses is a list of IP addresses that should be included as part of the Order - validation process. - This field must match the corresponding field on the DER encoded CSR. - type: array - items: - type: string - issuerRef: - description: |- - IssuerRef references a properly configured ACME-type Issuer which should - be used to create this Order. - If the Issuer does not exist, processing will be retried. - If the Issuer is not an 'ACME' Issuer, an error will be returned and the - Order will be marked as failed. - type: object - required: - - name - properties: - group: - description: Group of the resource being referred to. - type: string - kind: - description: Kind of the resource being referred to. - type: string - name: - description: Name of the resource being referred to. - type: string - request: - description: |- - Certificate signing request bytes in DER encoding. - This will be used when finalizing the order. - This field must be set on the order. - type: string - format: byte - status: - type: object - properties: - authorizations: - description: |- - Authorizations contains data returned from the ACME server on what - authorizations must be completed in order to validate the DNS names - specified on the Order. - type: array - items: - description: |- - ACMEAuthorization contains data returned from the ACME server on an - authorization that must be completed in order validate a DNS name on an ACME - Order resource. - type: object - required: - - url - properties: - challenges: - description: |- - Challenges specifies the challenge types offered by the ACME server. - One of these challenge types will be selected when validating the DNS - name and an appropriate Challenge resource will be created to perform - the ACME challenge process. - type: array - items: - description: |- - Challenge specifies a challenge offered by the ACME server for an Order. - An appropriate Challenge resource can be created to perform the ACME - challenge process. - type: object - required: - - token - - type - - url - properties: - token: - description: |- - Token is the token that must be presented for this challenge. - This is used to compute the 'key' that must also be presented. - type: string - type: - description: |- - Type is the type of challenge being offered, e.g. 'http-01', 'dns-01', - 'tls-sni-01', etc. - This is the raw value retrieved from the ACME server. - Only 'http-01' and 'dns-01' are supported by cert-manager, other values - will be ignored. - type: string - url: - description: |- - URL is the URL of this challenge. It can be used to retrieve additional - metadata about the Challenge from the ACME server. - type: string - identifier: - description: Identifier is the DNS name to be validated as part of this authorization - type: string - initialState: - description: |- - InitialState is the initial state of the ACME authorization when first - fetched from the ACME server. - If an Authorization is already 'valid', the Order controller will not - create a Challenge resource for the authorization. This will occur when - working with an ACME server that enables 'authz reuse' (such as Let's - Encrypt's production endpoint). - If not set and 'identifier' is set, the state is assumed to be pending - and a Challenge will be created. - type: string - enum: - - valid - - ready - - pending - - processing - - invalid - - expired - - errored - url: - description: URL is the URL of the Authorization that must be completed - type: string - wildcard: - description: |- - Wildcard will be true if this authorization is for a wildcard DNS name. - If this is true, the identifier will be the *non-wildcard* version of - the DNS name. - For example, if '*.example.com' is the DNS name being validated, this - field will be 'true' and the 'identifier' field will be 'example.com'. - type: boolean - certificate: - description: |- - Certificate is a copy of the PEM encoded certificate for this Order. - This field will be populated after the order has been successfully - finalized with the ACME server, and the order has transitioned to the - 'valid' state. - type: string - format: byte - failureTime: - description: |- - FailureTime stores the time that this order failed. - This is used to influence garbage collection and back-off. - type: string - format: date-time - finalizeURL: - description: |- - FinalizeURL of the Order. - This is used to obtain certificates for this order once it has been completed. - type: string - reason: - description: |- - Reason optionally provides more information about a why the order is in - the current state. - type: string - state: - description: |- - State contains the current state of this Order resource. - States 'success' and 'expired' are 'final' - type: string - enum: - - valid - - ready - - pending - - processing - - invalid - - expired - - errored - url: - description: |- - URL of the Order. - This will initially be empty when the resource is first created. - The Order controller will populate this field when the Order is first processed. - This field will be immutable after it is initially set. - type: string - served: true - storage: true - -# END crd {{- end }} diff --git a/packages/system/cert-manager/charts/cert-manager/templates/deployment.yaml b/packages/system/cert-manager/charts/cert-manager/templates/deployment.yaml index 8a4a9734..453e8238 100644 --- a/packages/system/cert-manager/charts/cert-manager/templates/deployment.yaml +++ b/packages/system/cert-manager/charts/cert-manager/templates/deployment.yaml @@ -66,6 +66,9 @@ spec: {{- with .Values.global.priorityClassName }} priorityClassName: {{ . | quote }} {{- end }} + {{- if (hasKey .Values.global "hostUsers") }} + hostUsers: {{ .Values.global.hostUsers }} + {{- end }} {{- with .Values.securityContext }} securityContext: {{- toYaml . | nindent 8 }} @@ -209,9 +212,13 @@ spec: failureThreshold: {{ .failureThreshold }} {{- end }} {{- end }} - {{- with .Values.nodeSelector }} + {{- $nodeSelector := .Values.global.nodeSelector | default dict }} + {{- $nodeSelector = merge $nodeSelector (.Values.nodeSelector | default dict) }} + {{- with $nodeSelector }} nodeSelector: - {{- toYaml . | nindent 8 }} + {{- range $key, $value := . }} + {{ $key }}: {{ $value | quote }} + {{- end }} {{- end }} {{- with .Values.affinity }} affinity: @@ -234,4 +241,4 @@ spec: {{- end }} {{- with .Values.hostAliases }} hostAliases: {{ toYaml . | nindent 8 }} - {{- end }} \ No newline at end of file + {{- end }} diff --git a/packages/system/cert-manager/charts/cert-manager/templates/rbac.yaml b/packages/system/cert-manager/charts/cert-manager/templates/rbac.yaml index baae425f..7acd5711 100644 --- a/packages/system/cert-manager/charts/cert-manager/templates/rbac.yaml +++ b/packages/system/cert-manager/charts/cert-manager/templates/rbac.yaml @@ -49,7 +49,7 @@ subjects: apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: - name: {{ template "cert-manager.serviceAccountName" . }}-tokenrequest + name: {{ template "cert-manager.fullname" . }}-tokenrequest namespace: {{ include "cert-manager.namespace" . }} labels: app: {{ include "cert-manager.name" . }} @@ -69,7 +69,7 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: - name: {{ include "cert-manager.fullname" . }}-{{ template "cert-manager.serviceAccountName" . }}-tokenrequest + name: {{ include "cert-manager.fullname" . }}-tokenrequest namespace: {{ include "cert-manager.namespace" . }} labels: app: {{ include "cert-manager.name" . }} @@ -80,7 +80,7 @@ metadata: roleRef: apiGroup: rbac.authorization.k8s.io kind: Role - name: {{ template "cert-manager.serviceAccountName" . }}-tokenrequest + name: {{ template "cert-manager.fullname" . }}-tokenrequest subjects: - kind: ServiceAccount name: {{ template "cert-manager.serviceAccountName" . }} @@ -256,8 +256,8 @@ rules: - apiGroups: ["networking.k8s.io"] resources: ["ingresses"] verbs: ["get", "list", "watch", "create", "delete", "update"] - - apiGroups: [ "gateway.networking.k8s.io" ] - resources: [ "httproutes" ] + - apiGroups: ["gateway.networking.k8s.io"] + resources: ["httproutes"] verbs: ["get", "list", "watch", "create", "delete", "update"] # We require the ability to specify a custom hostname when we are creating # new ingress resources. diff --git a/packages/system/cert-manager/charts/cert-manager/templates/serviceaccount.yaml b/packages/system/cert-manager/charts/cert-manager/templates/serviceaccount.yaml index 698ddef8..fac93d0a 100644 --- a/packages/system/cert-manager/charts/cert-manager/templates/serviceaccount.yaml +++ b/packages/system/cert-manager/charts/cert-manager/templates/serviceaccount.yaml @@ -12,7 +12,8 @@ metadata: {{- with .Values.serviceAccount.annotations }} annotations: {{- range $k, $v := . }} - {{- printf "%s: %s" (tpl $k $) (tpl $v $) | nindent 4 }} + {{- $value := $v | quote }} + {{- printf "%s: %s" (tpl $k $) (tpl $value $) | nindent 4 }} {{- end }} {{- end }} labels: diff --git a/packages/system/cert-manager/charts/cert-manager/templates/servicemonitor.yaml b/packages/system/cert-manager/charts/cert-manager/templates/servicemonitor.yaml index dd1beec8..a29f3c6a 100644 --- a/packages/system/cert-manager/charts/cert-manager/templates/servicemonitor.yaml +++ b/packages/system/cert-manager/charts/cert-manager/templates/servicemonitor.yaml @@ -16,7 +16,9 @@ metadata: app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/component: "controller" {{- include "labels" . | nindent 4 }} + {{- if .Values.prometheus.servicemonitor.prometheusInstance }} prometheus: {{ .Values.prometheus.servicemonitor.prometheusInstance }} + {{- end }} {{- with .Values.prometheus.servicemonitor.labels }} {{- toYaml . | nindent 4 }} {{- end }} @@ -54,8 +56,12 @@ spec: endpoints: - targetPort: {{ .Values.prometheus.servicemonitor.targetPort }} path: {{ .Values.prometheus.servicemonitor.path }} + {{- if .Values.prometheus.servicemonitor.interval }} interval: {{ .Values.prometheus.servicemonitor.interval }} + {{- end }} + {{- if .Values.prometheus.servicemonitor.scrapeTimeout }} scrapeTimeout: {{ .Values.prometheus.servicemonitor.scrapeTimeout }} + {{- end }} honorLabels: {{ .Values.prometheus.servicemonitor.honorLabels }} {{- with .Values.prometheus.servicemonitor.endpointAdditionalProperties }} {{- toYaml . | nindent 4 }} diff --git a/packages/system/cert-manager/charts/cert-manager/templates/startupapicheck-job.yaml b/packages/system/cert-manager/charts/cert-manager/templates/startupapicheck-job.yaml index 183cff4e..f68d5409 100644 --- a/packages/system/cert-manager/charts/cert-manager/templates/startupapicheck-job.yaml +++ b/packages/system/cert-manager/charts/cert-manager/templates/startupapicheck-job.yaml @@ -41,6 +41,9 @@ spec: {{- with .Values.global.priorityClassName }} priorityClassName: {{ . | quote }} {{- end }} + {{- if (hasKey .Values.global "hostUsers") }} + hostUsers: {{ .Values.global.hostUsers }} + {{- end }} {{- with .Values.startupapicheck.securityContext }} securityContext: {{- toYaml . | nindent 8 }} @@ -76,9 +79,13 @@ spec: volumeMounts: {{- toYaml . | nindent 12 }} {{- end }} - {{- with .Values.startupapicheck.nodeSelector }} + {{- $nodeSelector := .Values.global.nodeSelector | default dict }} + {{- $nodeSelector = merge $nodeSelector (.Values.startupapicheck.nodeSelector | default dict) }} + {{- with $nodeSelector }} nodeSelector: - {{- toYaml . | nindent 8 }} + {{- range $key, $value := . }} + {{ $key }}: {{ $value | quote }} + {{- end }} {{- end }} {{- with .Values.startupapicheck.affinity }} affinity: diff --git a/packages/system/cert-manager/charts/cert-manager/templates/webhook-deployment.yaml b/packages/system/cert-manager/charts/cert-manager/templates/webhook-deployment.yaml index 857cf353..d2b10ae8 100644 --- a/packages/system/cert-manager/charts/cert-manager/templates/webhook-deployment.yaml +++ b/packages/system/cert-manager/charts/cert-manager/templates/webhook-deployment.yaml @@ -66,6 +66,9 @@ spec: {{- with .Values.global.priorityClassName }} priorityClassName: {{ . | quote }} {{- end }} + {{- if (hasKey .Values.global "hostUsers") }} + hostUsers: {{ .Values.global.hostUsers }} + {{- end }} {{- with .Values.webhook.securityContext }} securityContext: {{- toYaml . | nindent 8 }} @@ -102,7 +105,7 @@ spec: - --dynamic-serving-dns-names={{ template "webhook.fullname" . }} - --dynamic-serving-dns-names={{ template "webhook.fullname" . }}.$(POD_NAMESPACE) - --dynamic-serving-dns-names={{ template "webhook.fullname" . }}.$(POD_NAMESPACE).svc - {{ if .Values.webhook.url.host }} + {{- if .Values.webhook.url.host }} - --dynamic-serving-dns-names={{ .Values.webhook.url.host }} {{- end }} {{- end }} @@ -137,11 +140,7 @@ spec: livenessProbe: httpGet: path: /livez - {{- if $config.healthzPort }} - port: {{ $config.healthzPort }} - {{- else }} - port: 6080 - {{- end }} + port: healthcheck scheme: HTTP initialDelaySeconds: {{ .Values.webhook.livenessProbe.initialDelaySeconds }} periodSeconds: {{ .Values.webhook.livenessProbe.periodSeconds }} @@ -151,11 +150,7 @@ spec: readinessProbe: httpGet: path: /healthz - {{- if $config.healthzPort }} - port: {{ $config.healthzPort }} - {{- else }} - port: 6080 - {{- end }} + port: healthcheck scheme: HTTP initialDelaySeconds: {{ .Values.webhook.readinessProbe.initialDelaySeconds }} periodSeconds: {{ .Values.webhook.readinessProbe.periodSeconds }} @@ -188,9 +183,13 @@ spec: {{- toYaml . | nindent 12 }} {{- end }} {{- end }} - {{- with .Values.webhook.nodeSelector }} + {{- $nodeSelector := .Values.global.nodeSelector | default dict }} + {{- $nodeSelector = merge $nodeSelector (.Values.webhook.nodeSelector | default dict) }} + {{- with $nodeSelector }} nodeSelector: - {{- toYaml . | nindent 8 }} + {{- range $key, $value := . }} + {{ $key }}: {{ $value | quote }} + {{- end }} {{- end }} {{- with .Values.webhook.affinity }} affinity: diff --git a/packages/system/cert-manager/charts/cert-manager/values.schema.json b/packages/system/cert-manager/charts/cert-manager/values.schema.json index 36d1d0ca..7f90b6c3 100644 --- a/packages/system/cert-manager/charts/cert-manager/values.schema.json +++ b/packages/system/cert-manager/charts/cert-manager/values.schema.json @@ -236,7 +236,7 @@ "issuers.cert-manager.io/*", "clusterissuers.cert-manager.io/*" ], - "description": "List of signer names that cert-manager will approve by default. CertificateRequests referencing these signer names will be auto-approved by cert-manager. Defaults to just approving the cert-manager.io Issuer and ClusterIssuer issuers. When set to an empty array, ALL issuers will be auto-approved by cert-manager. To disable the auto-approval, because eg. you are using approver-policy, you can enable 'disableAutoApproval'.\nref: https://cert-manager.io/docs/concepts/certificaterequest/#approval", + "description": "List of signer names that cert-manager will approve by default. CertificateRequests referencing these signer names will be auto-approved by cert-manager. Defaults to just approving the cert-manager.io Issuer and ClusterIssuer issuers. When set to an empty array, ALL issuers will be auto-approved by cert-manager. To disable the auto-approval, because, e.g., you are using approver-policy, you can enable 'disableAutoApproval'.\nref: https://cert-manager.io/docs/concepts/certificaterequest/#approval", "items": {}, "type": "array" }, @@ -461,10 +461,10 @@ "type": "boolean" }, "helm-values.cainjector.podDisruptionBudget.maxUnavailable": { - "description": "`maxUnavailable` configures the maximum unavailable pods for disruptions. It can either be set to\nan integer (e.g. 1) or a percentage value (e.g. 25%).\nCannot be used if `minAvailable` is set." + "description": "`maxUnavailable` configures the maximum unavailable pods for disruptions. It can either be set to\nan integer (e.g., 1) or a percentage value (e.g., 25%).\nCannot be used if `minAvailable` is set." }, "helm-values.cainjector.podDisruptionBudget.minAvailable": { - "description": "`minAvailable` configures the minimum available pods for disruptions. It can either be set to\nan integer (e.g. 1) or a percentage value (e.g. 25%).\nCannot be used if `maxUnavailable` is set." + "description": "`minAvailable` configures the minimum available pods for disruptions. It can either be set to\nan integer (e.g., 1) or a percentage value (e.g., 25%).\nCannot be used if `maxUnavailable` is set." }, "helm-values.cainjector.podLabels": { "default": {}, @@ -579,7 +579,7 @@ }, "helm-values.config": { "default": {}, - "description": "This property is used to configure options for the controller pod. This allows setting options that would usually be provided using flags.\n\nIf `apiVersion` and `kind` are unspecified they default to the current latest version (currently `controller.config.cert-manager.io/v1alpha1`). You can pin the version by specifying the `apiVersion` yourself.\n\nFor example:\nconfig:\n apiVersion: controller.config.cert-manager.io/v1alpha1\n kind: ControllerConfiguration\n logging:\n verbosity: 2\n format: text\n leaderElectionConfig:\n namespace: kube-system\n kubernetesAPIQPS: 9000\n kubernetesAPIBurst: 9000\n numberOfConcurrentWorkers: 200\n enableGatewayAPI: true\n # Feature gates as of v1.17.0. Listed with their default values.\n # See https://cert-manager.io/docs/cli/controller/\n featureGates:\n AdditionalCertificateOutputFormats: true # BETA - default=true\n AllAlpha: false # ALPHA - default=false\n AllBeta: false # BETA - default=false\n ExperimentalCertificateSigningRequestControllers: false # ALPHA - default=false\n ExperimentalGatewayAPISupport: true # BETA - default=true\n LiteralCertificateSubject: true # BETA - default=true\n NameConstraints: true # BETA - default=true\n OtherNames: false # ALPHA - default=false\n SecretsFilteredCaching: true # BETA - default=true\n ServerSideApply: false # ALPHA - default=false\n StableCertificateRequestName: true # BETA - default=true\n UseCertificateRequestBasicConstraints: false # ALPHA - default=false\n UseDomainQualifiedFinalizer: true # BETA - default=false\n ValidateCAA: false # ALPHA - default=false\n # Configure the metrics server for TLS\n # See https://cert-manager.io/docs/devops-tips/prometheus-metrics/#tls\n metricsTLSConfig:\n dynamic:\n secretNamespace: \"cert-manager\"\n secretName: \"cert-manager-metrics-ca\"\n dnsNames:\n - cert-manager-metrics", + "description": "This property is used to configure options for the controller pod. This allows setting options that would usually be provided using flags.\n\nIf `apiVersion` and `kind` are unspecified they default to the current latest version (currently `controller.config.cert-manager.io/v1alpha1`). You can pin the version by specifying the `apiVersion` yourself.\n\nFor example:\nconfig:\n apiVersion: controller.config.cert-manager.io/v1alpha1\n kind: ControllerConfiguration\n logging:\n verbosity: 2\n format: text\n leaderElectionConfig:\n namespace: kube-system\n kubernetesAPIQPS: 9000\n kubernetesAPIBurst: 9000\n numberOfConcurrentWorkers: 200\n enableGatewayAPI: true\n # Feature gates as of v1.18.1. Listed with their default values.\n # See https://cert-manager.io/docs/cli/controller/\n featureGates:\n AdditionalCertificateOutputFormats: true # GA - default=true\n AllAlpha: false # ALPHA - default=false\n AllBeta: false # BETA - default=false\n ExperimentalCertificateSigningRequestControllers: false # ALPHA - default=false\n ExperimentalGatewayAPISupport: true # BETA - default=true\n LiteralCertificateSubject: true # BETA - default=true\n NameConstraints: true # BETA - default=true\n OtherNames: false # ALPHA - default=false\n SecretsFilteredCaching: true # BETA - default=true\n ServerSideApply: false # ALPHA - default=false\n StableCertificateRequestName: true # BETA - default=true\n UseCertificateRequestBasicConstraints: false # ALPHA - default=false\n UseDomainQualifiedFinalizer: true # GA - default=true\n ValidateCAA: false # ALPHA - default=false\n DefaultPrivateKeyRotationPolicyAlways: true # BETA - default=true\n ACMEHTTP01IngressPathTypeExact: true # BETA - default=true\n # Configure the metrics server for TLS\n # See https://cert-manager.io/docs/devops-tips/prometheus-metrics/#tls\n metricsTLSConfig:\n dynamic:\n secretNamespace: \"cert-manager\"\n secretName: \"cert-manager-metrics-ca\"\n dnsNames:\n - cert-manager-metrics", "type": "object" }, "helm-values.containerSecurityContext": { @@ -689,6 +689,9 @@ "commonLabels": { "$ref": "#/$defs/helm-values.global.commonLabels" }, + "hostUsers": { + "$ref": "#/$defs/helm-values.global.hostUsers" + }, "imagePullSecrets": { "$ref": "#/$defs/helm-values.global.imagePullSecrets" }, @@ -698,6 +701,9 @@ "logLevel": { "$ref": "#/$defs/helm-values.global.logLevel" }, + "nodeSelector": { + "$ref": "#/$defs/helm-values.global.nodeSelector" + }, "podSecurityPolicy": { "$ref": "#/$defs/helm-values.global.podSecurityPolicy" }, @@ -718,6 +724,10 @@ "description": "Labels to apply to all resources.\nPlease note that this does not add labels to the resources created dynamically by the controllers. For these resources, you have to add the labels in the template in the cert-manager custom resource: For example, podTemplate/ ingressTemplate in ACMEChallengeSolverHTTP01Ingress. For more information, see the [cert-manager documentation](https://cert-manager.io/docs/reference/api-docs/#acme.cert-manager.io/v1.ACMEChallengeSolverHTTP01Ingress).\nFor example, secretTemplate in CertificateSpec\nFor more information, see the [cert-manager documentation](https://cert-manager.io/docs/reference/api-docs/#cert-manager.io/v1.CertificateSpec).", "type": "object" }, + "helm-values.global.hostUsers": { + "description": "Set all pods to run in a user namespace without host access. Experimental: may be removed once the Kubernetes User Namespaces feature is GA.\n\nRequirements:\n - Kubernetes ≥ 1.33, or\n - Kubernetes 1.27–1.32 with UserNamespacesSupport feature gate enabled.\n\nSet to false to run pods in a user namespace without host access.\n\nSee [limitations](https://kubernetes.io/docs/concepts/workloads/pods/user-namespaces/#limitations) for details.", + "type": "boolean" + }, "helm-values.global.imagePullSecrets": { "default": [], "description": "Reference to one or more secrets to be used when pulling images. For more information, see [Pull an Image from a Private Registry](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/).\n\nFor example:\nimagePullSecrets:\n - name: \"image-pull-secret\"", @@ -763,6 +773,11 @@ "description": "Set the verbosity of cert-manager. A range of 0 - 6, with 6 being the most verbose.", "type": "number" }, + "helm-values.global.nodeSelector": { + "default": {}, + "description": "Global node selector\n\nThe nodeSelector on Pods tells Kubernetes to schedule Pods on the nodes with matching labels. For more information, see [Assigning Pods to Nodes](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/).\n\nIf a component-specific nodeSelector is also set, it will be merged and take precedence.", + "type": "object" + }, "helm-values.global.podSecurityPolicy": { "properties": { "enabled": { @@ -921,7 +936,7 @@ "type": "number" }, "helm-values.nameOverride": { - "description": "Override the \"cert-manager.name\" value, which is used to annotate some of the resources that are created by this Chart (using \"app.kubernetes.io/name\"). NOTE: There are some inconsistencies in the Helm chart when it comes to these annotations (some resources use eg. \"cainjector.name\" which resolves to the value \"cainjector\").", + "description": "Override the \"cert-manager.name\" value, which is used to annotate some of the resources that are created by this Chart (using \"app.kubernetes.io/name\"). NOTE: There are some inconsistencies in the Helm chart when it comes to these annotations (some resources use, e.g., \"cainjector.name\" which resolves to the value \"cainjector\").", "type": "string" }, "helm-values.namespace": { @@ -965,10 +980,10 @@ "type": "boolean" }, "helm-values.podDisruptionBudget.maxUnavailable": { - "description": "This configures the maximum unavailable pods for disruptions. It can either be set to an integer (e.g. 1) or a percentage value (e.g. 25%). it cannot be used if `minAvailable` is set." + "description": "This configures the maximum unavailable pods for disruptions. It can either be set to an integer (e.g., 1) or a percentage value (e.g., 25%). it cannot be used if `minAvailable` is set." }, "helm-values.podDisruptionBudget.minAvailable": { - "description": "This configures the minimum available pods for disruptions. It can either be set to an integer (e.g. 1) or a percentage value (e.g. 25%).\nIt cannot be used if `maxUnavailable` is set." + "description": "This configures the minimum available pods for disruptions. It can either be set to an integer (e.g., 1) or a percentage value (e.g., 25%).\nIt cannot be used if `maxUnavailable` is set." }, "helm-values.podDnsConfig": { "description": "Pod DNS configuration. The podDnsConfig field is optional and can work with any podDnsPolicy settings. However, when a Pod's dnsPolicy is set to \"None\", the dnsConfig field has to be specified. For more information, see [Pod's DNS Config](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-dns-config).", @@ -1000,7 +1015,7 @@ }, "helm-values.prometheus.enabled": { "default": true, - "description": "Enable Prometheus monitoring for the cert-manager controller and webhook. If you use the Prometheus Operator, set prometheus.podmonitor.enabled or prometheus.servicemonitor.enabled, to create a PodMonitor or a\nServiceMonitor resource.\nOtherwise, 'prometheus.io' annotations are added to the cert-manager and cert-manager-webhook Deployments. Note that you can not enable both PodMonitor and ServiceMonitor as they are mutually exclusive. Enabling both will result in an error.", + "description": "Enable Prometheus monitoring for the cert-manager controller and webhook. If you use the Prometheus Operator, set prometheus.podmonitor.enabled or prometheus.servicemonitor.enabled, to create a PodMonitor or a\nServiceMonitor resource.\nOtherwise, 'prometheus.io' annotations are added to the cert-manager and cert-manager-webhook Deployments. Note that you cannot enable both PodMonitor and ServiceMonitor as they are mutually exclusive. Enabling both will result in an error.", "type": "boolean" }, "helm-values.prometheus.podmonitor": { @@ -1177,9 +1192,8 @@ "type": "string" }, "helm-values.prometheus.servicemonitor.targetPort": { - "default": 9402, - "description": "The target port to set on the ServiceMonitor. This must match the port that the cert-manager controller is listening on for metrics.", - "type": "number" + "default": "http-metrics", + "description": "The target port to set on the ServiceMonitor. This must match the port that the cert-manager controller is listening on for metrics." }, "helm-values.replicaCount": { "default": 1, @@ -1887,6 +1901,11 @@ "ipBlock": { "cidr": "0.0.0.0/0" } + }, + { + "ipBlock": { + "cidr": "::/0" + } } ] } @@ -1908,6 +1927,11 @@ "ipBlock": { "cidr": "0.0.0.0/0" } + }, + { + "ipBlock": { + "cidr": "::/0" + } } ] } @@ -1948,10 +1972,10 @@ "type": "boolean" }, "helm-values.webhook.podDisruptionBudget.maxUnavailable": { - "description": "This property configures the maximum unavailable pods for disruptions. Can either be set to an integer (e.g. 1) or a percentage value (e.g. 25%).\nIt cannot be used if `minAvailable` is set." + "description": "This property configures the maximum unavailable pods for disruptions. Can either be set to an integer (e.g., 1) or a percentage value (e.g., 25%).\nIt cannot be used if `minAvailable` is set." }, "helm-values.webhook.podDisruptionBudget.minAvailable": { - "description": "This property configures the minimum available pods for disruptions. Can either be set to an integer (e.g. 1) or a percentage value (e.g. 25%).\nIt cannot be used if `maxUnavailable` is set." + "description": "This property configures the minimum available pods for disruptions. Can either be set to an integer (e.g., 1) or a percentage value (e.g., 25%).\nIt cannot be used if `maxUnavailable` is set." }, "helm-values.webhook.podLabels": { "default": {}, diff --git a/packages/system/cert-manager/charts/cert-manager/values.yaml b/packages/system/cert-manager/charts/cert-manager/values.yaml index a8c94f8b..54257c79 100644 --- a/packages/system/cert-manager/charts/cert-manager/values.yaml +++ b/packages/system/cert-manager/charts/cert-manager/values.yaml @@ -12,6 +12,16 @@ global: # - name: "image-pull-secret" imagePullSecrets: [] + # Global node selector + # + # The nodeSelector on Pods tells Kubernetes to schedule Pods on the nodes with + # matching labels. + # For more information, see [Assigning Pods to Nodes](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). + # + # If a component-specific nodeSelector is also set, it will be merged and take precedence. + # +docs:property + nodeSelector: {} + # Labels to apply to all resources. # Please note that this does not add labels to the resources created dynamically by the controllers. # For these resources, you have to add the labels in the template in the cert-manager custom resource: @@ -28,6 +38,19 @@ global: # The optional priority class to be used for the cert-manager pods. priorityClassName: "" + # Set all pods to run in a user namespace without host access. + # Experimental: may be removed once the Kubernetes User Namespaces feature is GA. + # + # Requirements: + # - Kubernetes ≥ 1.33, or + # - Kubernetes 1.27–1.32 with UserNamespacesSupport feature gate enabled. + # + # Set to false to run pods in a user namespace without host access. + # + # See [limitations](https://kubernetes.io/docs/concepts/workloads/pods/user-namespaces/#limitations) for details. + # +docs:property + # hostUsers: false + rbac: # Create required ClusterRoles and ClusterRoleBindings for cert-manager. create: true @@ -117,14 +140,14 @@ podDisruptionBudget: enabled: false # This configures the minimum available pods for disruptions. It can either be set to - # an integer (e.g. 1) or a percentage value (e.g. 25%). + # an integer (e.g., 1) or a percentage value (e.g., 25%). # It cannot be used if `maxUnavailable` is set. # +docs:property # +docs:type=unknown # minAvailable: 1 # This configures the maximum unavailable pods for disruptions. It can either be set to - # an integer (e.g. 1) or a percentage value (e.g. 25%). + # an integer (e.g., 1) or a percentage value (e.g., 25%). # it cannot be used if `minAvailable` is set. # +docs:property # +docs:type=unknown @@ -176,7 +199,7 @@ namespace: "" # Override the "cert-manager.name" value, which is used to annotate some of # the resources that are created by this Chart (using "app.kubernetes.io/name"). # NOTE: There are some inconsistencies in the Helm chart when it comes to -# these annotations (some resources use eg. "cainjector.name" which resolves +# these annotations (some resources use, e.g., "cainjector.name" which resolves # to the value "cainjector"). # +docs:property # nameOverride: "my-cert-manager" @@ -231,10 +254,10 @@ enableCertificateOwnerRef: false # kubernetesAPIBurst: 9000 # numberOfConcurrentWorkers: 200 # enableGatewayAPI: true -# # Feature gates as of v1.17.0. Listed with their default values. +# # Feature gates as of v1.18.1. Listed with their default values. # # See https://cert-manager.io/docs/cli/controller/ # featureGates: -# AdditionalCertificateOutputFormats: true # BETA - default=true +# AdditionalCertificateOutputFormats: true # GA - default=true # AllAlpha: false # ALPHA - default=false # AllBeta: false # BETA - default=false # ExperimentalCertificateSigningRequestControllers: false # ALPHA - default=false @@ -246,8 +269,10 @@ enableCertificateOwnerRef: false # ServerSideApply: false # ALPHA - default=false # StableCertificateRequestName: true # BETA - default=true # UseCertificateRequestBasicConstraints: false # ALPHA - default=false -# UseDomainQualifiedFinalizer: true # BETA - default=false +# UseDomainQualifiedFinalizer: true # GA - default=true # ValidateCAA: false # ALPHA - default=false +# DefaultPrivateKeyRotationPolicyAlways: true # BETA - default=true +# ACMEHTTP01IngressPathTypeExact: true # BETA - default=true # # Configure the metrics server for TLS # # See https://cert-manager.io/docs/devops-tips/prometheus-metrics/#tls # metricsTLSConfig: @@ -278,7 +303,7 @@ disableAutoApproval: false # referencing these signer names will be auto-approved by cert-manager. Defaults to just # approving the cert-manager.io Issuer and ClusterIssuer issuers. When set to an empty # array, ALL issuers will be auto-approved by cert-manager. To disable the auto-approval, -# because eg. you are using approver-policy, you can enable 'disableAutoApproval'. +# because, e.g., you are using approver-policy, you can enable 'disableAutoApproval'. # ref: https://cert-manager.io/docs/concepts/certificaterequest/#approval # +docs:property approveSignerNames: @@ -434,7 +459,6 @@ ingressShim: {} # +docs:property # no_proxy: 127.0.0.1,localhost - # A Kubernetes Affinity, if required. For more information, see [Affinity v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#affinity-v1-core). # # For example: @@ -502,7 +526,7 @@ prometheus: # ServiceMonitor resource. # Otherwise, 'prometheus.io' annotations are added to the cert-manager and # cert-manager-webhook Deployments. - # Note that you can not enable both PodMonitor and ServiceMonitor as they are + # Note that you cannot enable both PodMonitor and ServiceMonitor as they are # mutually exclusive. Enabling both will result in an error. enabled: true @@ -522,7 +546,8 @@ prometheus: # The target port to set on the ServiceMonitor. This must match the port that the # cert-manager controller is listening on for metrics. - targetPort: 9402 + # +docs:type=string,integer + targetPort: http-metrics # The path to scrape for metrics. path: /metrics @@ -556,7 +581,7 @@ prometheus: # +docs:property endpointAdditionalProperties: {} - # Note that you can not enable both PodMonitor and ServiceMonitor as they are mutually exclusive. Enabling both will result in an error. + # Note that you cannot enable both PodMonitor and ServiceMonitor as they are mutually exclusive. Enabling both will result in an error. podmonitor: # Create a PodMonitor to add cert-manager to Prometheus. enabled: false @@ -706,14 +731,14 @@ webhook: enabled: false # This property configures the minimum available pods for disruptions. Can either be set to - # an integer (e.g. 1) or a percentage value (e.g. 25%). + # an integer (e.g., 1) or a percentage value (e.g., 25%). # It cannot be used if `maxUnavailable` is set. # +docs:property # +docs:type=unknown # minAvailable: 1 # This property configures the maximum unavailable pods for disruptions. Can either be set to - # an integer (e.g. 1) or a percentage value (e.g. 25%). + # an integer (e.g., 1) or a percentage value (e.g., 25%). # It cannot be used if `minAvailable` is set. # +docs:property # +docs:type=unknown @@ -959,6 +984,8 @@ webhook: - from: - ipBlock: cidr: 0.0.0.0/0 + - ipBlock: + cidr: "::/0" # Egress rule for the webhook network policy. By default, it allows all # outbound traffic to ports 80 and 443, as well as DNS ports. @@ -980,6 +1007,8 @@ webhook: to: - ipBlock: cidr: 0.0.0.0/0 + - ipBlock: + cidr: "::/0" # Additional volumes to add to the cert-manager controller pod. volumes: [] @@ -1073,14 +1102,14 @@ cainjector: enabled: false # `minAvailable` configures the minimum available pods for disruptions. It can either be set to - # an integer (e.g. 1) or a percentage value (e.g. 25%). + # an integer (e.g., 1) or a percentage value (e.g., 25%). # Cannot be used if `maxUnavailable` is set. # +docs:property # +docs:type=unknown # minAvailable: 1 # `maxUnavailable` configures the maximum unavailable pods for disruptions. It can either be set to - # an integer (e.g. 1) or a percentage value (e.g. 25%). + # an integer (e.g., 1) or a percentage value (e.g., 25%). # Cannot be used if `minAvailable` is set. # +docs:property # +docs:type=unknown diff --git a/packages/system/cert-manager/values.yaml b/packages/system/cert-manager/values.yaml index 5accf95f..e69de29b 100644 --- a/packages/system/cert-manager/values.yaml +++ b/packages/system/cert-manager/values.yaml @@ -1,2 +0,0 @@ -cert-manager: - installCRDs: false diff --git a/packages/system/cilium/Makefile b/packages/system/cilium/Makefile index 267c75cc..71b47a77 100644 --- a/packages/system/cilium/Makefile +++ b/packages/system/cilium/Makefile @@ -10,8 +10,17 @@ update: rm -rf charts helm repo add cilium https://helm.cilium.io/ helm repo update cilium - helm pull cilium/cilium --untar --untardir charts --version 1.18 + helm pull cilium/cilium --untar --untardir charts --version 1.19 $(SED_INPLACE) -e '/Used in iptables/d' -e '/SYS_MODULE/d' charts/cilium/values.yaml + # Drop the whole k8s<1.30 AppArmor annotations block from the cilium-agent + # DaemonSet. Cozystack manages these annotations through cilium.podAnnotations + # in values.yaml on every k8s version, so keeping the upstream block would + # produce duplicate mapping keys on k8s<1.30. + perl -i -0pe 's|\n \{\{- if not \.Values\.securityContext\.privileged \}\}\n \{\{- if semverCompare "<1\.30\.0".*?\n \{\{- end \}\}\n \{\{- end \}\}\n \{\{- end \}\}||s' \ + charts/cilium/templates/cilium-agent/daemonset.yaml + @! grep -q 'container\.apparmor\.security\.beta\.kubernetes\.io' \ + charts/cilium/templates/cilium-agent/daemonset.yaml || \ + { echo 'ERROR: perl patch did not remove the upstream AppArmor block from cilium-agent/daemonset.yaml (upstream template format may have changed)' >&2; exit 1; } version=$$(awk '$$1 == "version:" {print $$2}' charts/cilium/Chart.yaml) && \ $(SED_INPLACE) "s/ARG VERSION=.*/ARG VERSION=v$${version}/" images/cilium/Dockerfile diff --git a/packages/system/cilium/charts/cilium/Chart.yaml b/packages/system/cilium/charts/cilium/Chart.yaml index 0de0d9c7..5fe2baed 100644 --- a/packages/system/cilium/charts/cilium/Chart.yaml +++ b/packages/system/cilium/charts/cilium/Chart.yaml @@ -41,11 +41,8 @@ annotations: namespace context.\n- kind: CiliumNodeConfig\n version: v2\n name: ciliumnodeconfigs.cilium.io\n \ displayName: Cilium Node Configuration\n description: |\n CiliumNodeConfig is a list of configuration key-value pairs. It is applied to\n nodes indicated - by a label selector.\n- kind: CiliumBGPPeeringPolicy\n version: v2alpha1\n name: - ciliumbgppeeringpolicies.cilium.io\n displayName: Cilium BGP Peering Policy\n - \ description: |\n Cilium BGP Peering Policy instructs Cilium to create specific - BGP peering\n configurations.\n- kind: CiliumBGPClusterConfig\n version: v2alpha1\n - \ name: ciliumbgpclusterconfigs.cilium.io\n displayName: Cilium BGP Cluster Config\n + by a label selector.\n- kind: CiliumBGPClusterConfig\n version: v2alpha1\n name: + ciliumbgpclusterconfigs.cilium.io\n displayName: Cilium BGP Cluster Config\n \ description: |\n Cilium BGP Cluster Config instructs Cilium operator to create specific BGP cluster\n configurations.\n- kind: CiliumBGPPeerConfig\n version: v2alpha1\n name: ciliumbgppeerconfigs.cilium.io\n displayName: Cilium BGP Peer @@ -79,7 +76,7 @@ annotations: Cilium Gateway Class Config\n description: |\n CiliumGatewayClassConfig defines a configuration for Gateway API GatewayClass.\n" apiVersion: v2 -appVersion: 1.18.6 +appVersion: 1.19.3 description: eBPF-based Networking, Security, and Observability home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg @@ -95,4 +92,4 @@ kubeVersion: '>= 1.21.0-0' name: cilium sources: - https://github.com/cilium/cilium -version: 1.18.6 +version: 1.19.3 diff --git a/packages/system/cilium/charts/cilium/README.md b/packages/system/cilium/charts/cilium/README.md index 840d0126..d7697c56 100644 --- a/packages/system/cilium/charts/cilium/README.md +++ b/packages/system/cilium/charts/cilium/README.md @@ -1,6 +1,6 @@ # cilium -![Version: 1.18.6](https://img.shields.io/badge/Version-1.18.6-informational?style=flat-square) ![AppVersion: 1.18.6](https://img.shields.io/badge/AppVersion-1.18.6-informational?style=flat-square) +![Version: 1.19.3](https://img.shields.io/badge/Version-1.19.3-informational?style=flat-square) ![AppVersion: 1.19.3](https://img.shields.io/badge/AppVersion-1.19.3-informational?style=flat-square) Cilium is open source software for providing and transparently securing network connectivity and loadbalancing between application workloads such as @@ -59,10 +59,14 @@ contributors across the globe, there is almost always someone available to help. | agentNotReadyTaintKey | string | `"node.cilium.io/agent-not-ready"` | Configure the key of the taint indicating that Cilium is not ready on the node. When set to a value starting with `ignore-taint.cluster-autoscaler.kubernetes.io/`, the Cluster Autoscaler will ignore the taint on its decisions, allowing the cluster to scale up. | | aksbyocni.enabled | bool | `false` | Enable AKS BYOCNI integration. Note that this is incompatible with AKS clusters not created in BYOCNI mode: use Azure integration (`azure.enabled`) instead. | | alibabacloud.enabled | bool | `false` | Enable AlibabaCloud ENI integration | +| alibabacloud.nodeSpec.securityGroupTags | list | `[]` | | +| alibabacloud.nodeSpec.securityGroups | list | `[]` | | +| alibabacloud.nodeSpec.vSwitchTags | list | `[]` | | +| alibabacloud.nodeSpec.vSwitches | list | `[]` | | | annotateK8sNode | bool | `false` | Annotate k8s node upon initialization with Cilium's metadata. | | annotations | object | `{}` | Annotations to be added to all top-level cilium-agent objects (resources under templates/cilium-agent) | | apiRateLimit | string | `nil` | The api-rate-limit option can be used to overwrite individual settings of the default configuration for rate limiting calls to the Cilium Agent API | -| authentication.enabled | bool | `true` | Enable authentication processing and garbage collection. Note that if disabled, policy enforcement will still block requests that require authentication. But the resulting authentication requests for these requests will not be processed, therefore the requests not be allowed. | +| authentication.enabled | bool | `false` | Enable authentication processing and garbage collection. Note that if disabled, policy enforcement will still block requests that require authentication. But the resulting authentication requests for these requests will not be processed, therefore the requests not be allowed. | | authentication.gcInterval | string | `"5m0s"` | Interval for garbage collection of auth map entries. | | authentication.mutual.connectTimeout | string | `"5s"` | Timeout for connecting to the remote node TCP socket | | authentication.mutual.port | int | `4250` | Port on the agent where mutual authentication handshakes between agents will be performed | @@ -73,7 +77,7 @@ contributors across the globe, there is almost always someone available to help. | authentication.mutual.spire.enabled | bool | `false` | Enable SPIRE integration (beta) | | authentication.mutual.spire.install.agent.affinity | object | `{}` | SPIRE agent affinity configuration | | authentication.mutual.spire.install.agent.annotations | object | `{}` | SPIRE agent annotations | -| authentication.mutual.spire.install.agent.image | object | `{"digest":"sha256:163970884fba18860cac93655dc32b6af85a5dcf2ebb7e3e119a10888eff8fcd","override":null,"pullPolicy":"IfNotPresent","repository":"ghcr.io/spiffe/spire-agent","tag":"1.12.4","useDigest":true}` | SPIRE agent image | +| authentication.mutual.spire.install.agent.image | object | `{"digest":"sha256:5106ac601272a88684db14daf7f54b9a45f31f77bb16a906bd5e87756ee7b97c","override":null,"pullPolicy":"IfNotPresent","repository":"ghcr.io/spiffe/spire-agent","tag":"1.9.6","useDigest":true}` | SPIRE agent image | | authentication.mutual.spire.install.agent.labels | object | `{}` | SPIRE agent labels | | authentication.mutual.spire.install.agent.nodeSelector | object | `{}` | SPIRE agent nodeSelector configuration ref: ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector | | authentication.mutual.spire.install.agent.podSecurityContext | object | `{}` | Security context to be added to spire agent pods. SecurityContext holds pod-level security attributes and common container settings. ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod | @@ -85,7 +89,7 @@ contributors across the globe, there is almost always someone available to help. | authentication.mutual.spire.install.agent.tolerations | list | `[{"effect":"NoSchedule","key":"node.kubernetes.io/not-ready"},{"effect":"NoSchedule","key":"node-role.kubernetes.io/master"},{"effect":"NoSchedule","key":"node-role.kubernetes.io/control-plane"},{"effect":"NoSchedule","key":"node.cloudprovider.kubernetes.io/uninitialized","value":"true"},{"key":"CriticalAddonsOnly","operator":"Exists"}]` | SPIRE agent tolerations configuration By default it follows the same tolerations as the agent itself to allow the Cilium agent on this node to connect to SPIRE. ref: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ | | authentication.mutual.spire.install.enabled | bool | `true` | Enable SPIRE installation. This will only take effect only if authentication.mutual.spire.enabled is true | | authentication.mutual.spire.install.existingNamespace | bool | `false` | SPIRE namespace already exists. Set to true if Helm should not create, manage, and import the SPIRE namespace. | -| authentication.mutual.spire.install.initImage | object | `{"digest":"sha256:2383baad1860bbe9d8a7a843775048fd07d8afe292b94bd876df64a69aae7cb1","override":null,"pullPolicy":"IfNotPresent","repository":"docker.io/library/busybox","tag":"1.37.0","useDigest":true}` | init container image of SPIRE agent and server | +| authentication.mutual.spire.install.initImage | object | `{"digest":"sha256:1487d0af5f52b4ba31c7e465126ee2123fe3f2305d638e7827681e7cf6c83d5e","override":null,"pullPolicy":"IfNotPresent","repository":"docker.io/library/busybox","tag":"1.37.0","useDigest":true}` | init container image of SPIRE agent and server | | authentication.mutual.spire.install.namespace | string | `"cilium-spire"` | SPIRE namespace to install into | | authentication.mutual.spire.install.server.affinity | object | `{}` | SPIRE server affinity configuration | | authentication.mutual.spire.install.server.annotations | object | `{}` | SPIRE server annotations | @@ -95,7 +99,7 @@ contributors across the globe, there is almost always someone available to help. | authentication.mutual.spire.install.server.dataStorage.enabled | bool | `true` | Enable SPIRE server data storage | | authentication.mutual.spire.install.server.dataStorage.size | string | `"1Gi"` | Size of the SPIRE server data storage | | authentication.mutual.spire.install.server.dataStorage.storageClass | string | `nil` | StorageClass of the SPIRE server data storage | -| authentication.mutual.spire.install.server.image | object | `{"digest":"sha256:34147f27066ab2be5cc10ca1d4bfd361144196467155d46c45f3519f41596e49","override":null,"pullPolicy":"IfNotPresent","repository":"ghcr.io/spiffe/spire-server","tag":"1.12.4","useDigest":true}` | SPIRE server image | +| authentication.mutual.spire.install.server.image | object | `{"digest":"sha256:59a0b92b39773515e25e68a46c40d3b931b9c1860bc445a79ceb45a805cab8b4","override":null,"pullPolicy":"IfNotPresent","repository":"ghcr.io/spiffe/spire-server","tag":"1.9.6","useDigest":true}` | SPIRE server image | | authentication.mutual.spire.install.server.initContainers | list | `[]` | SPIRE server init containers | | authentication.mutual.spire.install.server.labels | object | `{}` | SPIRE server labels | | authentication.mutual.spire.install.server.nodeSelector | object | `{}` | SPIRE server nodeSelector configuration ref: ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector | @@ -114,13 +118,14 @@ contributors across the globe, there is almost always someone available to help. | authentication.rotatedIdentitiesQueueSize | int | `1024` | Buffer size of the channel Cilium uses to receive certificate expiration events from auth handlers. | | autoDirectNodeRoutes | bool | `false` | Enable installation of PodCIDR routes between worker nodes if worker nodes share a common L2 network segment. | | azure.enabled | bool | `false` | Enable Azure integration. Note that this is incompatible with AKS clusters created in BYOCNI mode: use AKS BYOCNI integration (`aksbyocni.enabled`) instead. | +| azure.nodeSpec.azureInterfaceName | string | `""` | | | bandwidthManager | object | `{"bbr":false,"bbrHostNamespaceOnly":false,"enabled":false}` | Enable bandwidth manager to optimize TCP and UDP workloads and allow for rate-limiting traffic from individual Pods with EDT (Earliest Departure Time) through the "kubernetes.io/egress-bandwidth" Pod annotation. | | bandwidthManager.bbr | bool | `false` | Activate BBR TCP congestion control for Pods | | bandwidthManager.bbrHostNamespaceOnly | bool | `false` | Activate BBR TCP congestion control for Pods in the host namespace only. | | bandwidthManager.enabled | bool | `false` | Enable bandwidth manager infrastructure (also prerequirement for BBR) | -| bgpControlPlane | object | `{"enabled":false,"legacyOriginAttribute":{"enabled":false},"routerIDAllocation":{"ipPool":"","mode":"default"},"secretsNamespace":{"create":false,"name":"kube-system"},"statusReport":{"enabled":true}}` | This feature set enables virtual BGP routers to be created via CiliumBGPPeeringPolicy CRDs. | +| bgpControlPlane | object | `{"enabled":false,"legacyOriginAttribute":{"enabled":false},"routerIDAllocation":{"ipPool":"","mode":"default"},"secretsNamespace":{"create":false,"name":"kube-system"},"statusReport":{"enabled":true}}` | This feature set enables virtual BGP routers to be created via BGP CRDs. | | bgpControlPlane.enabled | bool | `false` | Enables the BGP control plane. | -| bgpControlPlane.legacyOriginAttribute | object | `{"enabled":false}` | Legacy BGP ORIGIN attribute settings (BGPv2 only) | +| bgpControlPlane.legacyOriginAttribute | object | `{"enabled":false}` | Legacy BGP ORIGIN attribute settings | | bgpControlPlane.legacyOriginAttribute.enabled | bool | `false` | Enable/Disable advertising LoadBalancerIP routes with the legacy BGP ORIGIN attribute value INCOMPLETE (2) instead of the default IGP (0). Enable for compatibility with the legacy behavior of MetalLB integration. | | bgpControlPlane.routerIDAllocation | object | `{"ipPool":"","mode":"default"}` | BGP router-id allocation mode | | bgpControlPlane.routerIDAllocation.ipPool | string | `""` | IP pool to allocate the BGP router-id from when the mode is ip-pool. | @@ -128,20 +133,20 @@ contributors across the globe, there is almost always someone available to help. | bgpControlPlane.secretsNamespace | object | `{"create":false,"name":"kube-system"}` | SecretsNamespace is the namespace which BGP support will retrieve secrets from. | | bgpControlPlane.secretsNamespace.create | bool | `false` | Create secrets namespace for BGP secrets. | | bgpControlPlane.secretsNamespace.name | string | `"kube-system"` | The name of the secret namespace to which Cilium agents are given read access | -| bgpControlPlane.statusReport | object | `{"enabled":true}` | Status reporting settings (BGPv2 only) | -| bgpControlPlane.statusReport.enabled | bool | `true` | Enable/Disable BGPv2 status reporting It is recommended to enable status reporting in general, but if you have any issue such as high API server load, you can disable it by setting this to false. | +| bgpControlPlane.statusReport | object | `{"enabled":true}` | Status reporting settings | +| bgpControlPlane.statusReport.enabled | bool | `true` | Enable/Disable BGP status reporting It is recommended to enable status reporting in general, but if you have any issue such as high API server load, you can disable it by setting this to false. | | bpf.authMapMax | int | `524288` | Configure the maximum number of entries in auth map. | | bpf.autoMount.enabled | bool | `true` | Enable automatic mount of BPF filesystem When `autoMount` is enabled, the BPF filesystem is mounted at `bpf.root` path on the underlying host and inside the cilium agent pod. If users disable `autoMount`, it's expected that users have mounted bpffs filesystem at the specified `bpf.root` volume, and then the volume will be mounted inside the cilium agent pod at the same path. | | bpf.ctAccounting | bool | `false` | Enable CT accounting for packets and bytes | | bpf.ctAnyMax | int | `262144` | Configure the maximum number of entries for the non-TCP connection tracking table. | | bpf.ctTcpMax | int | `524288` | Configure the maximum number of entries in the TCP connection tracking table. | -| bpf.datapathMode | string | `veth` | Mode for Pod devices for the core datapath (veth, netkit, netkit-l2) | +| bpf.datapathMode | string | `veth` | Mode for Pod devices for the core datapath (veth, netkit, netkit-l2). Note netkit is incompatible with TPROXY (`bpf.tproxy`). | | bpf.disableExternalIPMitigation | bool | `false` | Disable ExternalIP mitigation (CVE-2020-8554) | | bpf.distributedLRU | object | `{"enabled":false}` | Control to use a distributed per-CPU backend memory for the core BPF LRU maps which Cilium uses. This improves performance significantly, but it is also recommended to increase BPF map sizing along with that. | | bpf.distributedLRU.enabled | bool | `false` | Enable distributed LRU backend memory. For compatibility with existing installations it is off by default. | | bpf.enableTCX | bool | `true` | Attach endpoint programs using tcx instead of legacy tc hooks on supported kernels. | | bpf.events | object | `{"default":{"burstLimit":null,"rateLimit":null},"drop":{"enabled":true},"policyVerdict":{"enabled":true},"trace":{"enabled":true}}` | Control events generated by the Cilium datapath exposed to Cilium monitor and Hubble. Helm configuration for BPF events map rate limiting is experimental and might change in upcoming releases. | -| bpf.events.default | object | `{"burstLimit":null,"rateLimit":null}` | Default settings for all types of events except dbg and pcap. | +| bpf.events.default | object | `{"burstLimit":null,"rateLimit":null}` | Default settings for all types of events except dbg. | | bpf.events.default.burstLimit | int | `0` | Configure the maximum number of messages that can be written to BPF events map in 1 second. If burstLimit is greater than 0, non-zero value for rateLimit must also be provided lest the configuration is considered invalid. Setting both burstLimit and rateLimit to 0 disables BPF events rate limiting. | | bpf.events.default.rateLimit | int | `0` | Configure the limit of messages per second that can be written to BPF events map. The number of messages is averaged, meaning that if no messages were written to the map over 5 seconds, it's possible to write more events in the 6th second. If rateLimit is greater than 0, non-zero value for burstLimit must also be provided lest the configuration is considered invalid. Setting both burstLimit and rateLimit to 0 disables BPF events rate limiting. | | bpf.events.drop.enabled | bool | `true` | Enable drop events. | @@ -158,19 +163,23 @@ contributors across the globe, there is almost always someone available to help. | bpf.monitorAggregation | string | `"medium"` | Configure the level of aggregation for monitor notifications. Valid options are none, low, medium, maximum. | | bpf.monitorFlags | string | `"all"` | Configure which TCP flags trigger notifications when seen for the first time in a connection. | | bpf.monitorInterval | string | `"5s"` | Configure the typical time between monitor notifications for active connections. | +| bpf.monitorTraceIPOption | int | `0` | Configure the IP tracing option type. This option is used to specify the IP option type to use for tracing. The value must be an integer between 0 and 255. @schema type: [null, integer] minimum: 0 maximum: 255 @schema | | bpf.natMax | int | `524288` | Configure the maximum number of entries for the NAT table. | | bpf.neighMax | int | `524288` | Configure the maximum number of entries for the neighbor table. | | bpf.nodeMapMax | int | `nil` | Configures the maximum number of entries for the node table. | | bpf.policyMapMax | int | `16384` | Configure the maximum number of entries in endpoint policy map (per endpoint). @schema type: [null, integer] @schema | +| bpf.policyMapPressureMetricsThreshold | float64 | `0.1` | Configure threshold for emitting pressure metrics of policy maps. @schema type: [null, number] @schema | | bpf.policyStatsMapMax | int | `65536` | Configure the maximum number of entries in global policy stats map. @schema type: [null, integer] @schema | | bpf.preallocateMaps | bool | `false` | Enables pre-allocation of eBPF map values. This increases memory usage but can reduce latency. | | bpf.root | string | `"/sys/fs/bpf"` | Configure the mount point for the BPF filesystem | -| bpf.tproxy | bool | `false` | Configure the eBPF-based TPROXY (beta) to reduce reliance on iptables rules for implementing Layer 7 policy. | +| bpf.tproxy | bool | `false` | Configure the eBPF-based TPROXY (beta) to reduce reliance on iptables rules for implementing Layer 7 policy. Note this is incompatible with netkit (`bpf.datapathMode=netkit`, `bpf.datapathMode=netkit-l2`). | | bpf.vlanBypass | list | `[]` | Configure explicitly allowed VLAN id's for bpf logic bypass. [0] will allow all VLAN id's without any filtering. | | bpfClockProbe | bool | `false` | Enable BPF clock source probing for more efficient tick retrieval. | -| certgen | object | `{"affinity":{},"annotations":{"cronJob":{},"job":{}},"extraVolumeMounts":[],"extraVolumes":[],"generateCA":true,"image":{"digest":"sha256:2825dbfa6f89cbed882fd1d81e46a56c087e35885825139923aa29eb8aec47a9","override":null,"pullPolicy":"IfNotPresent","repository":"quay.io/cilium/certgen","tag":"v0.3.1","useDigest":true},"nodeSelector":{},"podLabels":{},"priorityClassName":"","resources":{},"tolerations":[],"ttlSecondsAfterFinished":1800}` | Configure certificate generation for Hubble integration. If hubble.tls.auto.method=cronJob, these values are used for the Kubernetes CronJob which will be scheduled regularly to (re)generate any certificates not provided manually. | +| certgen | object | `{"affinity":{},"annotations":{"cronJob":{},"job":{}},"cronJob":{"failedJobsHistoryLimit":1,"successfulJobsHistoryLimit":3},"extraVolumeMounts":[],"extraVolumes":[],"generateCA":true,"image":{"digest":"sha256:f0c656830e856d26b24b0e144df1f8b327d3b46748d76a630514111fc365b697","override":null,"pullPolicy":"IfNotPresent","repository":"quay.io/cilium/certgen","tag":"v0.4.1","useDigest":true},"nodeSelector":{},"podLabels":{},"priorityClassName":"","resources":{},"tolerations":[],"ttlSecondsAfterFinished":null}` | Configure certificate generation for Hubble integration. If hubble.tls.auto.method=cronJob, these values are used for the Kubernetes CronJob which will be scheduled regularly to (re)generate any certificates not provided manually. | | certgen.affinity | object | `{}` | Affinity for certgen | | certgen.annotations | object | `{"cronJob":{},"job":{}}` | Annotations to be added to the hubble-certgen initial Job and CronJob | +| certgen.cronJob.failedJobsHistoryLimit | int | `1` | The number of failed finished jobs to keep | +| certgen.cronJob.successfulJobsHistoryLimit | int | `3` | The number of successful finished jobs to keep | | certgen.extraVolumeMounts | list | `[]` | Additional certgen volumeMounts. | | certgen.extraVolumes | list | `[]` | Additional certgen volumes. | | certgen.generateCA | bool | `true` | When set to true the certificate authority secret is created. | @@ -179,7 +188,7 @@ contributors across the globe, there is almost always someone available to help. | certgen.priorityClassName | string | `""` | Priority class for certgen ref: https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/#priorityclass | | certgen.resources | object | `{}` | Resource limits for certgen ref: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers | | certgen.tolerations | list | `[]` | Node tolerations for pod assignment on nodes with taints ref: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ | -| certgen.ttlSecondsAfterFinished | int | `1800` | Seconds after which the completed job pod will be deleted | +| certgen.ttlSecondsAfterFinished | string | `nil` | Seconds after which the completed job pod will be deleted | | cgroup | object | `{"autoMount":{"enabled":true,"resources":{}},"hostRoot":"/run/cilium/cgroupv2"}` | Configure cgroup related configuration | | cgroup.autoMount.enabled | bool | `true` | Enable auto mount of cgroup2 filesystem. When `autoMount` is enabled, cgroup2 filesystem is mounted at `cgroup.hostRoot` path on the underlying host and inside the cilium agent pod. If users disable `autoMount`, it's expected that users have mounted cgroup2 filesystem at the specified `cgroup.hostRoot` volume, and then the volume will be mounted inside the cilium agent pod at the same path. | | cgroup.autoMount.resources | object | `{}` | Init Container Cgroup Automount resource limits & requests | @@ -205,7 +214,7 @@ contributors across the globe, there is almost always someone available to help. | clustermesh.apiserver.extraVolumeMounts | list | `[]` | Additional clustermesh-apiserver volumeMounts. | | clustermesh.apiserver.extraVolumes | list | `[]` | Additional clustermesh-apiserver volumes. | | clustermesh.apiserver.healthPort | int | `9880` | TCP port for the clustermesh-apiserver health API. | -| clustermesh.apiserver.image | object | `{"digest":"sha256:8ee142912a0e261850c0802d9256ddbe3729e1cd35c6bea2d93077f334c3cf3b","override":null,"pullPolicy":"IfNotPresent","repository":"quay.io/cilium/clustermesh-apiserver","tag":"v1.18.6","useDigest":true}` | Clustermesh API server image. | +| clustermesh.apiserver.image | object | `{"digest":"sha256:a8136a7615d6c6041d3aa6f2674d17beaec238170d669507ccc05328a778e2b7","override":null,"pullPolicy":"IfNotPresent","repository":"quay.io/cilium/clustermesh-apiserver","tag":"v1.19.3","useDigest":true}` | Clustermesh API server image. | | clustermesh.apiserver.kvstoremesh.enabled | bool | `true` | Enable KVStoreMesh. KVStoreMesh caches the information retrieved from the remote clusters in the local etcd instance (deprecated - KVStoreMesh will always be enabled once the option is removed). | | clustermesh.apiserver.kvstoremesh.extraArgs | list | `[]` | Additional KVStoreMesh arguments. | | clustermesh.apiserver.kvstoremesh.extraEnv | list | `[]` | Additional KVStoreMesh environment variables. | @@ -255,38 +264,65 @@ contributors across the globe, there is almost always someone available to help. | clustermesh.apiserver.service.annotations | object | `{}` | Annotations for the clustermesh-apiserver service. Example annotations to configure an internal load balancer on different cloud providers: * AKS: service.beta.kubernetes.io/azure-load-balancer-internal: "true" * EKS: service.beta.kubernetes.io/aws-load-balancer-scheme: "internal" * GKE: networking.gke.io/load-balancer-type: "Internal" | | clustermesh.apiserver.service.enableSessionAffinity | string | `"HAOnly"` | Defines when to enable session affinity. Each replica in a clustermesh-apiserver deployment runs its own discrete etcd cluster. Remote clients connect to one of the replicas through a shared Kubernetes Service. A client reconnecting to a different backend will require a full resync to ensure data integrity. Session affinity can reduce the likelihood of this happening, but may not be supported by all cloud providers. Possible values: - "HAOnly" (default) Only enable session affinity for deployments with more than 1 replica. - "Always" Always enable session affinity. - "Never" Never enable session affinity. Useful in environments where session affinity is not supported, but may lead to slightly degraded performance due to more frequent reconnections. | | clustermesh.apiserver.service.externalTrafficPolicy | string | `"Cluster"` | The externalTrafficPolicy of service used for apiserver access. | +| clustermesh.apiserver.service.externallyCreated | bool | `false` | Set externallyCreated to true to create the clustermesh-apiserver service outside this helm chart. For example after external load balancer controllers are created. | | clustermesh.apiserver.service.internalTrafficPolicy | string | `"Cluster"` | The internalTrafficPolicy of service used for apiserver access. | | clustermesh.apiserver.service.labels | object | `{}` | Labels for the clustermesh-apiserver service. | | clustermesh.apiserver.service.loadBalancerClass | string | `nil` | Configure a loadBalancerClass. Allows to configure the loadBalancerClass on the clustermesh-apiserver LB service in case the Service type is set to LoadBalancer (requires Kubernetes 1.24+). | | clustermesh.apiserver.service.loadBalancerIP | string | `nil` | Configure a specific loadBalancerIP. Allows to configure a specific loadBalancerIP on the clustermesh-apiserver LB service in case the Service type is set to LoadBalancer. | | clustermesh.apiserver.service.loadBalancerSourceRanges | list | `[]` | Configure loadBalancerSourceRanges. Allows to configure the source IP ranges allowed to access the clustermesh-apiserver LB service in case the Service type is set to LoadBalancer. | -| clustermesh.apiserver.service.nodePort | int | `32379` | Optional port to use as the node port for apiserver access. WARNING: make sure to configure a different NodePort in each cluster if kube-proxy replacement is enabled, as Cilium is currently affected by a known bug (#24692) when NodePorts are handled by the KPR implementation. If a service with the same NodePort exists both in the local and the remote cluster, all traffic originating from inside the cluster and targeting the corresponding NodePort will be redirected to a local backend, regardless of whether the destination node belongs to the local or the remote cluster. | +| clustermesh.apiserver.service.nodePort | int | `32379` | Optional port to use as the node port for apiserver access. | | clustermesh.apiserver.service.type | string | `"NodePort"` | The type of service used for apiserver access. | | clustermesh.apiserver.terminationGracePeriodSeconds | int | `30` | terminationGracePeriodSeconds for the clustermesh-apiserver deployment | | clustermesh.apiserver.tls.admin | object | `{"cert":"","key":""}` | base64 encoded PEM values for the clustermesh-apiserver admin certificate and private key. Used if 'auto' is not enabled. | -| clustermesh.apiserver.tls.authMode | string | `"legacy"` | Configure the clustermesh authentication mode. Supported values: - legacy: All clusters access remote clustermesh instances with the same username (i.e., remote). The "remote" certificate must be generated with CN=remote if provided manually. - migration: Intermediate mode required to upgrade from legacy to cluster (and vice versa) with no disruption. Specifically, it enables the creation of the per-cluster usernames, while still using the common one for authentication. The "remote" certificate must be generated with CN=remote if provided manually (same as legacy). - cluster: Each cluster accesses remote etcd instances with a username depending on the local cluster name (i.e., remote-). The "remote" certificate must be generated with CN=remote- if provided manually. Cluster mode is meaningful only when the same CA is shared across all clusters part of the mesh. | +| clustermesh.apiserver.tls.admin.cert | string | `""` | Deprecated, as secrets will always need to be created externally if `auto` is disabled. | +| clustermesh.apiserver.tls.admin.key | string | `""` | Deprecated, as secrets will always need to be created externally if `auto` is disabled. | +| clustermesh.apiserver.tls.authMode | string | `"migration"` | Configure the clustermesh authentication mode. Supported values: - legacy: All clusters access remote clustermesh instances with the same username (i.e., remote). The "remote" certificate must be generated with CN=remote if provided manually. - migration: Intermediate mode required to upgrade from legacy to cluster (and vice versa) with no disruption. Specifically, it enables the creation of the per-cluster usernames, while still using the common one for authentication. The "remote" certificate must be generated with CN=remote if provided manually (same as legacy). - cluster: Each cluster accesses remote etcd instances with a username depending on the local cluster name (i.e., remote-). The "remote" certificate must be generated with CN=remote- if provided manually. Cluster mode is meaningful only when the same CA is shared across all clusters part of the mesh. | | clustermesh.apiserver.tls.auto | object | `{"certManagerIssuerRef":{},"certValidityDuration":1095,"enabled":true,"method":"helm"}` | Configure automatic TLS certificates generation. A Kubernetes CronJob is used the generate any certificates not provided by the user at installation time. | | clustermesh.apiserver.tls.auto.certManagerIssuerRef | object | `{}` | certmanager issuer used when clustermesh.apiserver.tls.auto.method=certmanager. | | clustermesh.apiserver.tls.auto.certValidityDuration | int | `1095` | Generated certificates validity duration in days. | -| clustermesh.apiserver.tls.auto.enabled | bool | `true` | When set to true, automatically generate a CA and certificates to enable mTLS between clustermesh-apiserver and external workload instances. If set to false, the certs to be provided by setting appropriate values below. | -| clustermesh.apiserver.tls.client | object | `{"cert":"","key":""}` | base64 encoded PEM values for the clustermesh-apiserver client certificate and private key. Used if 'auto' is not enabled. | -| clustermesh.apiserver.tls.enableSecrets | bool | `true` | Allow users to provide their own certificates Users may need to provide their certificates using a mechanism that requires they provide their own secrets. This setting does not apply to any of the auto-generated mechanisms below, it only restricts the creation of secrets via the `tls-provided` templates. | +| clustermesh.apiserver.tls.auto.enabled | bool | `true` | When set to true, automatically generate a CA and certificates to enable mTLS between clustermesh-apiserver and external workload instances. When set to false you need to pre-create the following secrets: - clustermesh-apiserver-server-cert - clustermesh-apiserver-admin-cert - clustermesh-apiserver-remote-cert - clustermesh-apiserver-local-cert The above secret should at least contains the keys `tls.crt` and `tls.key` and optionally `ca.crt` if a CA bundle is not configured. | +| clustermesh.apiserver.tls.enableSecrets | deprecated | `true` | Allow users to provide their own certificates Users may need to provide their certificates using a mechanism that requires they provide their own secrets. This setting does not apply to any of the auto-generated mechanisms below, it only restricts the creation of secrets via the `tls-provided` templates. This option is deprecated as secrets are expected to be created externally when 'auto' is not enabled. | | clustermesh.apiserver.tls.remote | object | `{"cert":"","key":""}` | base64 encoded PEM values for the clustermesh-apiserver remote cluster certificate and private key. Used if 'auto' is not enabled. | +| clustermesh.apiserver.tls.remote.cert | string | `""` | Deprecated, as secrets will always need to be created externally if `auto` is disabled. | +| clustermesh.apiserver.tls.remote.key | string | `""` | Deprecated, as secrets will always need to be created externally if `auto` is disabled. | | clustermesh.apiserver.tls.server | object | `{"cert":"","extraDnsNames":[],"extraIpAddresses":[],"key":""}` | base64 encoded PEM values for the clustermesh-apiserver server certificate and private key. Used if 'auto' is not enabled. | +| clustermesh.apiserver.tls.server.cert | string | `""` | Deprecated, as secrets will always need to be created externally if `auto` is disabled. | | clustermesh.apiserver.tls.server.extraDnsNames | list | `[]` | Extra DNS names added to certificate when it's auto generated | | clustermesh.apiserver.tls.server.extraIpAddresses | list | `[]` | Extra IP addresses added to certificate when it's auto generated | +| clustermesh.apiserver.tls.server.key | string | `""` | Deprecated, as secrets will always need to be created externally if `auto` is disabled. | | clustermesh.apiserver.tolerations | list | `[]` | Node tolerations for pod assignment on nodes with taints ref: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ | | clustermesh.apiserver.topologySpreadConstraints | list | `[]` | Pod topology spread constraints for clustermesh-apiserver | | clustermesh.apiserver.updateStrategy | object | `{"rollingUpdate":{"maxSurge":1,"maxUnavailable":0},"type":"RollingUpdate"}` | clustermesh-apiserver update strategy | +| clustermesh.cacheTTL | string | `"0s"` | The time to live for the cache of a remote cluster after connectivity is lost. If the connection is not re-established within this duration, the cached data is revoked to prevent stale state. If not specified or set to 0s, the cache is never revoked (default). | | clustermesh.config | object | `{"clusters":[],"domain":"mesh.cilium.io","enabled":false}` | Clustermesh explicit configuration. | -| clustermesh.config.clusters | list | `[]` | List of clusters to be peered in the mesh. | +| clustermesh.config.clusters | list | `[]` | Clusters to be peered in the mesh. @schema type: [object, array] @schema | | clustermesh.config.domain | string | `"mesh.cilium.io"` | Default dns domain for the Clustermesh API servers This is used in the case cluster addresses are not provided and IPs are used. | -| clustermesh.config.enabled | bool | `false` | Enable the Clustermesh explicit configuration. | +| clustermesh.config.enabled | bool | `false` | Enable the Clustermesh explicit configuration. If set to false, you need to provide the following resources yourself: - (Secret) cilium-clustermesh (used by cilium-agent/cilium-operator to connect to the local etcd instance if KVStoreMesh is enabled or the remote clusters if KVStoreMesh is disabled) - (Secret) cilium-kvstoremesh (used by KVStoreMesh to connect to the remote clusters) - (ConfigMap) clustermesh-remote-users (used to create one etcd user per remote cluster if clustermesh-apiserver is used and `clustermesh.apiserver.tls.authMode` is not set to `legacy`) | | clustermesh.enableEndpointSliceSynchronization | bool | `false` | Enable the synchronization of Kubernetes EndpointSlices corresponding to the remote endpoints of appropriately-annotated global services through ClusterMesh | -| clustermesh.enableMCSAPISupport | bool | `false` | Enable Multi-Cluster Services API support | +| clustermesh.enableMCSAPISupport | bool | `false` | Enable Multi-Cluster Services API support (deprecated; use clustermesh.mcsapi.enabled) | | clustermesh.maxConnectedClusters | int | `255` | The maximum number of clusters to support in a ClusterMesh. This value cannot be changed on running clusters, and all clusters in a ClusterMesh must be configured with the same value. Values > 255 will decrease the maximum allocatable cluster-local identities. Supported values are 255 and 511. | -| clustermesh.policyDefaultLocalCluster | bool | `false` | Control whether policy rules assume by default the local cluster if not explicitly selected | -| clustermesh.useAPIServer | bool | `false` | Deploy clustermesh-apiserver for clustermesh | +| clustermesh.mcsapi.corednsAutoConfigure.affinity | object | `{}` | Affinity for coredns-mcsapi-autoconfig | +| clustermesh.mcsapi.corednsAutoConfigure.annotations | object | `{}` | Annotations to be added to the coredns-mcsapi-autoconfig Job | +| clustermesh.mcsapi.corednsAutoConfigure.coredns.clusterDomain | string | `"cluster.local"` | The cluster domain for the cluster CoreDNS service | +| clustermesh.mcsapi.corednsAutoConfigure.coredns.clustersetDomain | string | `"clusterset.local"` | The clusterset domain for the cluster CoreDNS service | +| clustermesh.mcsapi.corednsAutoConfigure.coredns.configMapName | string | `"coredns"` | The ConfigMap name for the cluster CoreDNS service | +| clustermesh.mcsapi.corednsAutoConfigure.coredns.deploymentName | string | `"coredns"` | The Deployment for the cluster CoreDNS service | +| clustermesh.mcsapi.corednsAutoConfigure.coredns.namespace | string | `"kube-system"` | The namespace for the cluster CoreDNS service | +| clustermesh.mcsapi.corednsAutoConfigure.coredns.serviceAccountName | string | `"coredns"` | The Service Account name for the cluster CoreDNS service | +| clustermesh.mcsapi.corednsAutoConfigure.enabled | bool | `false` | Enable auto-configuration of CoreDNS for Multi-Cluster Services API. CoreDNS MUST be at least in version v1.12.2 to run this. | +| clustermesh.mcsapi.corednsAutoConfigure.extraArgs | list | `[]` | Additional arguments to `clustermesh-apiserver coredns-mcsapi-auto-configure`. | +| clustermesh.mcsapi.corednsAutoConfigure.extraVolumeMounts | list | `[]` | Additional coredns-mcsapi-autoconfig volumeMounts. | +| clustermesh.mcsapi.corednsAutoConfigure.extraVolumes | list | `[]` | Additional coredns-mcsapi-autoconfig volumes. | +| clustermesh.mcsapi.corednsAutoConfigure.nodeSelector | object | `{}` | Node selector for coredns-mcsapi-autoconfig ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector | +| clustermesh.mcsapi.corednsAutoConfigure.podLabels | object | `{}` | Labels to be added to coredns-mcsapi-autoconfig pods | +| clustermesh.mcsapi.corednsAutoConfigure.priorityClassName | string | `""` | Priority class for coredns-mcsapi-autoconfig ref: https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/#priorityclass | +| clustermesh.mcsapi.corednsAutoConfigure.resources | object | `{}` | Resource limits for coredns-mcsapi-autoconfig ref: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers | +| clustermesh.mcsapi.corednsAutoConfigure.tolerations | list | `[]` | Node tolerations for pod assignment on nodes with taints ref: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ | +| clustermesh.mcsapi.corednsAutoConfigure.ttlSecondsAfterFinished | int | `1800` | Seconds after which the completed job pod will be deleted | +| clustermesh.mcsapi.enabled | bool | `false` | Enable Multi-Cluster Services API support | +| clustermesh.mcsapi.installCRDs | bool | `true` | Enabled MCS-API CRDs auto-installation | +| clustermesh.policyDefaultLocalCluster | bool | `true` | Control whether policy rules assume by default the local cluster if not explicitly selected | +| clustermesh.useAPIServer | bool | `false` | Deploy clustermesh-apiserver for clustermesh. This option is typically used with ``clustermesh.config.enabled=true``. Refer to the ``clustermesh.config.enabled=true``documentation for more information. | | cni.binPath | string | `"/opt/cni/bin"` | Configure the path to the CNI binary directory on the host. | | cni.chainingMode | string | `nil` | Configure chaining on top of other CNI plugins. Possible values: - none - aws-cni - flannel - generic-veth - portmap | | cni.chainingTarget | string | `nil` | A CNI network name in to which the Cilium plugin should be added as a chained plugin. This will cause the agent to watch for a CNI network with this network name. When it is found, this will be used as the basis for Cilium's CNI configuration file. If this is set, it assumes a chaining mode of generic-veth. As a special case, a chaining mode of aws-cni implies a chainingTarget of aws-cni. | @@ -301,15 +337,17 @@ contributors across the globe, there is almost always someone available to help. | cni.install | bool | `true` | Install the CNI configuration and binary files into the filesystem. | | cni.iptablesRemoveAWSRules | bool | `true` | Enable the removal of iptables rules created by the AWS CNI VPC plugin. | | cni.logFile | string | `"/var/run/cilium/cilium-cni.log"` | Configure the log file for CNI logging with retention policy of 7 days. Disable CNI file logging by setting this field to empty explicitly. | -| cni.resources | object | `{"requests":{"cpu":"100m","memory":"10Mi"}}` | Specifies the resources for the cni initContainer | +| cni.resources | object | `{"limits":{"cpu":1,"memory":"1Gi"},"requests":{"cpu":"100m","memory":"10Mi"}}` | Specifies the resources for the cni initContainer | | cni.uninstall | bool | `false` | Remove the CNI configuration and binary files on agent shutdown. Enable this if you're removing Cilium from the cluster. Disable this to prevent the CNI configuration file from being removed during agent upgrade, which can cause nodes to go unmanageable. | | commonLabels | object | `{}` | commonLabels allows users to add common labels for all Cilium resources. | +| configDriftDetection | object | `{"driftChecker":true,"enabled":true,"ignoredKeys":[]}` | Configuration for the ConfigMap drift detection feature. When enabled, the agent continuously watches the cilium-config ConfigMap and exposes a cilium_drift_checker_config_delta Prometheus metric reporting the number of keys that differ between the ConfigMap and the agent's active settings. A non-zero value indicates that the agent has not yet applied all current ConfigMap changes and needs to be restarted. | +| configDriftDetection.driftChecker | bool | `true` | Enable the drift checker which compares the DynamicConfig table against the agent's active settings and publishes the cilium_drift_checker_config_delta metric. | +| configDriftDetection.enabled | bool | `true` | Enable watching of the cilium-config ConfigMap and reflecting its contents into the agent's internal DynamicConfig table. | +| configDriftDetection.ignoredKeys | list | `[]` | List of config-map keys to ignore when computing the drift delta. | | connectivityProbeFrequencyRatio | float64 | `0.5` | Ratio of the connectivity probe frequency vs resource usage, a float in [0, 1]. 0 will give more frequent probing, 1 will give less frequent probing. Probing frequency is dynamically adjusted based on the cluster size. | | conntrackGCInterval | string | `"0s"` | Configure how frequently garbage collection should occur for the datapath connection tracking table. | | conntrackGCMaxInterval | string | `""` | Configure the maximum frequency for the garbage collection of the connection tracking table. Only affects the automatic computation for the frequency and has no effect when 'conntrackGCInterval' is set. This can be set to more frequently clean up unused identities created from ToFQDN policies. | | crdWaitTimeout | string | `"5m"` | Configure timeout in which Cilium will exit if CRDs are not available | -| customCalls | object | `{"enabled":false}` | Tail call hooks for custom eBPF programs. | -| customCalls.enabled | bool | `false` | Enable tail call hooks for custom eBPF programs. | | daemon.allowedConfigOverrides | string | `nil` | allowedConfigOverrides is a list of config-map keys that can be overridden. That is to say, if this value is set, config sources (excepting the first one) can only override keys in this list. This takes precedence over blockedConfigOverrides. By default, all keys may be overridden. To disable overrides, set this to "none" or change the configSources variable. | | daemon.blockedConfigOverrides | string | `nil` | blockedConfigOverrides is a list of config-map keys that may not be overridden. In other words, if any of these keys appear in a configuration source excepting the first one, they will be ignored This is ignored if allowedConfigOverrides is set. By default, all keys may be overridden. | | daemon.configSources | string | `nil` | Configure a custom list of possible configuration override sources The default is "config-map:cilium-config,cilium-node-config". For supported values, see the help text for the build-config subcommand. Note that this value should be a comma-separated string. | @@ -318,8 +356,8 @@ contributors across the globe, there is almost always someone available to help. | dashboards | object | `{"annotations":{},"enabled":false,"label":"grafana_dashboard","labelValue":"1","namespace":null}` | Grafana dashboards for cilium-agent grafana can import dashboards based on the label and value ref: https://github.com/grafana/helm-charts/tree/main/charts/grafana#sidecar-for-dashboards | | debug.enabled | bool | `false` | Enable debug logging | | debug.metricsSamplingInterval | string | `"5m"` | Set the agent-internal metrics sampling frequency. This sets the frequency of the internal sampling of the agent metrics. These are available via the "cilium-dbg shell -- metrics -s" command and are part of the metrics HTML page included in the sysdump. @schema type: [null, string] @schema | -| debug.verbose | string | `nil` | Configure verbosity levels for debug logging This option is used to enable debug messages for operations related to such sub-system such as (e.g. kvstore, envoy, datapath or policy), and flow is for enabling debug messages emitted per request, message and connection. Multiple values can be set via a space-separated string (e.g. "datapath envoy"). Applicable values: - flow - kvstore - envoy - datapath - policy | -| defaultLBServiceIPAM | string | `"lbipam"` | defaultLBServiceIPAM indicates the default LoadBalancer Service IPAM when no LoadBalancer class is set. Applicable values: lbipam, nodeipam, none @schema type: [string] @schema | +| debug.verbose | string | `nil` | Configure verbosity levels for debug logging This option is used to enable debug messages for operations related to such sub-system such as (e.g. kvstore, envoy, datapath, policy, or tagged), and flow is for enabling debug messages emitted per request, message and connection. Multiple values can be set via a space-separated string (e.g. "datapath envoy"). Applicable values: - flow - kvstore - envoy - datapath - policy - tagged | +| defaultLBServiceIPAM | string | `"lbipam"` | defaultLBServiceIPAM indicates the default LoadBalancer Service IPAM when no LoadBalancer class is set. Applicable values: lbipam, nodeipam, none | | directRoutingSkipUnreachable | bool | `false` | Enable skipping of PodCIDR routes between worker nodes if the worker nodes are in a different L2 network segment. | | disableEndpointCRD | bool | `false` | Disable the usage of CiliumEndpoint CRD. | | dnsPolicy | string | `""` | DNS policy for Cilium agent pods. Ref: https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy | @@ -338,12 +376,13 @@ contributors across the globe, there is almost always someone available to help. | egressGateway.reconciliationTriggerInterval | string | `"1s"` | Time between triggers of egress gateway state reconciliations | | enableCriticalPriorityClass | bool | `true` | Explicitly enable or disable priority class. .Capabilities.KubeVersion is unsettable in `helm template` calls, it depends on k8s libraries version that Helm was compiled against. This option allows to explicitly disable setting the priority class, which is useful for rendering charts for gke clusters in advance. | | enableIPv4BIGTCP | bool | `false` | Enables IPv4 BIG TCP support which increases maximum IPv4 GSO/GRO limits for nodes and pods | -| enableIPv4Masquerade | bool | `true` unless ipam eni mode is active | Enables masquerading of IPv4 traffic leaving the node from endpoints. | +| enableIPv4Masquerade | bool | `true` unless ipam eni mode is active | Enables masquerading of IPv4 traffic leaving the node from endpoints. | | enableIPv6BIGTCP | bool | `false` | Enables IPv6 BIG TCP support which increases maximum IPv6 GSO/GRO limits for nodes and pods | | enableIPv6Masquerade | bool | `true` | Enables masquerading of IPv6 traffic leaving the node from endpoints. | | enableInternalTrafficPolicy | bool | `true` | Enable Internal Traffic Policy | | enableLBIPAM | bool | `true` | Enable LoadBalancer IP Address Management | | enableMasqueradeRouteSource | bool | `false` | Enables masquerading to the source of the route for traffic leaving the node from endpoints. | +| enableNoServiceEndpointsRoutable | bool | `true` | Enable routing to a service that has zero endpoints | | enableNonDefaultDenyPolicies | bool | `true` | Enable Non-Default-Deny policies | | enableXTSocketFallback | bool | `true` | Enables the fallback compatibility solution for when the xt_socket kernel module is missing and it is needed for the datapath L7 redirection to work properly. See documentation for details on when this can be disabled: https://docs.cilium.io/en/stable/operations/system_requirements/#linux-kernel. | | encryption.enabled | bool | `false` | Enable transparent network encryption. | @@ -355,14 +394,39 @@ contributors across the globe, there is almost always someone available to help. | encryption.ipsec.mountPath | string | `"/etc/ipsec"` | Path to mount the secret inside the Cilium pod. | | encryption.ipsec.secretName | string | `"cilium-ipsec-keys"` | Name of the Kubernetes secret containing the encryption keys. | | encryption.nodeEncryption | bool | `false` | Enable encryption for pure node to node traffic. This option is only effective when encryption.type is set to "wireguard". | -| encryption.strictMode | object | `{"allowRemoteNodeIdentities":false,"cidr":"","enabled":false}` | Configure the WireGuard Pod2Pod strict mode. | -| encryption.strictMode.allowRemoteNodeIdentities | bool | `false` | Allow dynamic lookup of remote node identities. This is required when tunneling is used or direct routing is used and the node CIDR and pod CIDR overlap. | -| encryption.strictMode.cidr | string | `""` | CIDR for the WireGuard Pod2Pod strict mode. | -| encryption.strictMode.enabled | bool | `false` | Enable WireGuard Pod2Pod strict mode. | -| encryption.type | string | `"ipsec"` | Encryption method. Can be either ipsec or wireguard. | +| encryption.strictMode | object | `{"allowRemoteNodeIdentities":false,"cidr":"","egress":{"allowRemoteNodeIdentities":false,"cidr":"","enabled":false},"enabled":false,"ingress":{"enabled":false}}` | Configure the Encryption Pod2Pod strict mode. | +| encryption.strictMode.allowRemoteNodeIdentities | bool | `false` | Allow dynamic lookup of remote node identities. (deprecated: please use encryption.strictMode.egress.allowRemoteNodeIdentities) This is required when tunneling is used or direct routing is used and the node CIDR and pod CIDR overlap. | +| encryption.strictMode.cidr | string | `""` | CIDR for the Encryption Pod2Pod strict mode. (deprecated: please use encryption.strictMode.egress.cidr) | +| encryption.strictMode.egress.allowRemoteNodeIdentities | bool | `false` | Allow dynamic lookup of remote node identities. This is required when tunneling is used or direct routing is used and the node CIDR and pod CIDR overlap. | +| encryption.strictMode.egress.cidr | string | `""` | CIDR for the Encryption Pod2Pod strict egress mode. | +| encryption.strictMode.egress.enabled | bool | `false` | Enable strict egress encryption. | +| encryption.strictMode.enabled | bool | `false` | Enable Encryption Pod2Pod strict mode. (deprecated: please use encryption.strictMode.egress.enabled) | +| encryption.strictMode.ingress.enabled | bool | `false` | Enable strict ingress encryption. When enabled, all unencrypted overlay ingress traffic will be dropped. This option is only applicable when WireGuard and tunneling are enabled. | +| encryption.type | string | `"ipsec"` | Encryption method. Can be one of ipsec, wireguard or ztunnel. | | encryption.wireguard.persistentKeepalive | string | `"0s"` | Controls WireGuard PersistentKeepalive option. Set 0s to disable. | +| encryption.ztunnel | object | `{"affinity":{},"annotations":{},"caAddress":"https://localhost:15012","extraEnv":[],"extraVolumeMounts":[],"extraVolumes":[],"healthPort":15021,"image":{"digest":null,"override":null,"pullPolicy":"IfNotPresent","repository":"docker.io/istio/ztunnel","tag":"1.28.0-distroless","useDigest":false},"nodeSelector":{"kubernetes.io/os":"linux"},"podAnnotations":{},"podLabels":{},"priorityClassName":null,"readinessProbe":{"failureThreshold":3,"initialDelaySeconds":0,"periodSeconds":10},"resources":{"requests":{"cpu":"200m","memory":"512Mi"}},"secrets":{"bootstrapRootCert":null},"terminationGracePeriodSeconds":30,"tolerations":[{"effect":"NoSchedule","operator":"Exists"},{"key":"CriticalAddonsOnly","operator":"Exists"},{"effect":"NoExecute","operator":"Exists"}],"updateStrategy":{"rollingUpdate":{"maxSurge":1,"maxUnavailable":0},"type":"RollingUpdate"}}` | ztunnel encryption configuration. ztunnel is Istio's purpose-built, per-node proxy for handling L4 traffic in ambient mesh mode. These settings only apply when encryption.type is set to "ztunnel". | +| encryption.ztunnel.affinity | object | `{}` | Affinity for ztunnel pods. | +| encryption.ztunnel.annotations | object | `{}` | Annotations to be added to all ztunnel resources. | +| encryption.ztunnel.caAddress | string | `"https://localhost:15012"` | CA server address for certificate requests. | +| encryption.ztunnel.extraEnv | list | `[]` | Additional ztunnel container environment variables. | +| encryption.ztunnel.extraVolumeMounts | list | `[]` | Additional ztunnel volumeMounts. | +| encryption.ztunnel.extraVolumes | list | `[]` | Additional ztunnel volumes. | +| encryption.ztunnel.healthPort | int | `15021` | TCP port for the health API. | +| encryption.ztunnel.image | object | `{"digest":null,"override":null,"pullPolicy":"IfNotPresent","repository":"docker.io/istio/ztunnel","tag":"1.28.0-distroless","useDigest":false}` | ztunnel container image. | +| encryption.ztunnel.nodeSelector | object | `{"kubernetes.io/os":"linux"}` | Node selector for ztunnel pods. | +| encryption.ztunnel.podAnnotations | object | `{}` | Annotations to be added to ztunnel pods. | +| encryption.ztunnel.podLabels | object | `{}` | Labels to be added to ztunnel pods. | +| encryption.ztunnel.priorityClassName | string | `nil` | The priority class to use for ztunnel pods. | +| encryption.ztunnel.readinessProbe | object | `{"failureThreshold":3,"initialDelaySeconds":0,"periodSeconds":10}` | Readiness probe configuration. | +| encryption.ztunnel.resources | object | `{"requests":{"cpu":"200m","memory":"512Mi"}}` | ztunnel resource limits & requests. | +| encryption.ztunnel.secrets | object | `{"bootstrapRootCert":null}` | ztunnel secrets configuration. | +| encryption.ztunnel.secrets.bootstrapRootCert | string | `nil` | Base64-encoded bootstrap root certificate content. If not provided, the secret must be created manually before deploying. @schema type: [null, string] @schema | +| encryption.ztunnel.terminationGracePeriodSeconds | int | `30` | Configure termination grace period for ztunnel DaemonSet. | +| encryption.ztunnel.tolerations | list | `[{"effect":"NoSchedule","operator":"Exists"},{"key":"CriticalAddonsOnly","operator":"Exists"},{"effect":"NoExecute","operator":"Exists"}]` | Node tolerations for ztunnel scheduling. | +| encryption.ztunnel.updateStrategy | object | `{"rollingUpdate":{"maxSurge":1,"maxUnavailable":0},"type":"RollingUpdate"}` | ztunnel update strategy. | | endpointHealthChecking.enabled | bool | `true` | Enable connectivity health checking between virtual endpoints. | | endpointLockdownOnMapOverflow | bool | `false` | Enable endpoint lockdown on policy map overflow. | +| endpointPolicyUpdateTimeoutDuration | string | `nil` | Max duration to wait for envoy to respond to configuration changes. Default "10s". | | endpointRoutes.enabled | bool | `false` | Enable use of per endpoint routes instead of routing via the cilium_host interface. | | eni.awsEnablePrefixDelegation | bool | `false` | Enable ENI prefix delegation | | eni.awsReleaseExcessIPs | bool | `false` | Release IPs not used from the ENI | @@ -373,12 +437,24 @@ contributors across the globe, there is almost always someone available to help. | eni.gcTags | object | `{"io.cilium/cilium-managed":"true,"io.cilium/cluster-name":""}` | Additional tags attached to ENIs created by Cilium. Dangling ENIs with this tag will be garbage collected | | eni.iamRole | string | `""` | If using IAM role for Service Accounts will not try to inject identity values from cilium-aws kubernetes secret. Adds annotation to service account if managed by Helm. See https://github.com/aws/amazon-eks-pod-identity-webhook | | eni.instanceTagsFilter | list | `[]` | Filter via AWS EC2 Instance tags (k=v) which will dictate which AWS EC2 Instances are going to be used to create new ENIs | +| eni.nodeSpec | object | `{"deleteOnTermination":null,"disablePrefixDelegation":false,"excludeInterfaceTags":[],"firstInterfaceIndex":null,"securityGroupTags":[],"securityGroups":[],"subnetIDs":[],"subnetTags":[],"usePrimaryAddress":false}` | NodeSpec configuration for the ENI | +| eni.nodeSpec.deleteOnTermination | string | `nil` | Delete ENI on termination @schema type: [null, boolean] @schema | +| eni.nodeSpec.disablePrefixDelegation | bool | `false` | Disable prefix delegation for IP allocation | +| eni.nodeSpec.excludeInterfaceTags | list | `[]` | Exclude interface tags to use for IP allocation | +| eni.nodeSpec.firstInterfaceIndex | string | `nil` | First interface index to use for IP allocation @schema type: [null, integer] @schema | +| eni.nodeSpec.securityGroupTags | list | `[]` | Security group tags to use for IP allocation | +| eni.nodeSpec.securityGroups | list | `[]` | Security groups to use for IP allocation | +| eni.nodeSpec.subnetIDs | list | `[]` | Subnet IDs to use for IP allocation | +| eni.nodeSpec.subnetTags | list | `[]` | Subnet tags to use for IP allocation | +| eni.nodeSpec.usePrimaryAddress | bool | `false` | Use primary address for IP allocation | | eni.subnetIDsFilter | list | `[]` | Filter via subnet IDs which will dictate which subnets are going to be used to create new ENIs Important note: This requires that each instance has an ENI with a matching subnet attached when Cilium is deployed. If you only want to control subnets for ENIs attached by Cilium, use the CNI configuration file settings (cni.customConf) instead. | | eni.subnetTagsFilter | list | `[]` | Filter via tags (k=v) which will dictate which subnets are going to be used to create new ENIs Important note: This requires that each instance has an ENI with a matching subnet attached when Cilium is deployed. If you only want to control subnets for ENIs attached by Cilium, use the CNI configuration file settings (cni.customConf) instead. | | envoy.affinity | object | `{"nodeAffinity":{"requiredDuringSchedulingIgnoredDuringExecution":{"nodeSelectorTerms":[{"matchExpressions":[{"key":"cilium.io/no-schedule","operator":"NotIn","values":["true"]}]}]}},"podAffinity":{"requiredDuringSchedulingIgnoredDuringExecution":[{"labelSelector":{"matchLabels":{"k8s-app":"cilium"}},"topologyKey":"kubernetes.io/hostname"}]},"podAntiAffinity":{"requiredDuringSchedulingIgnoredDuringExecution":[{"labelSelector":{"matchLabels":{"k8s-app":"cilium-envoy"}},"topologyKey":"kubernetes.io/hostname"}]}}` | Affinity for cilium-envoy. | | envoy.annotations | object | `{}` | Annotations to be added to all top-level cilium-envoy objects (resources under templates/cilium-envoy) | | envoy.baseID | int | `0` | Set Envoy'--base-id' to use when allocating shared memory regions. Only needs to be changed if multiple Envoy instances will run on the same node and may have conflicts. Supported values: 0 - 4294967295. Defaults to '0' | | envoy.bootstrapConfigMap | string | `nil` | ADVANCED OPTION: Bring your own custom Envoy bootstrap ConfigMap. Provide the name of a ConfigMap with a `bootstrap-config.json` key. When specified, Envoy will use this ConfigMap instead of the default provided by the chart. WARNING: Use of this setting has the potential to prevent cilium-envoy from starting up, and can cause unexpected behavior (e.g. due to syntax error or semantically incorrect configuration). Before submitting an issue, please ensure you have disabled this feature, as support cannot be provided for custom Envoy bootstrap configs. @schema type: [null, string] @schema | +| envoy.clusterMaxConnections | int | `1024` | Maximum number of connections on Envoy clusters | +| envoy.clusterMaxRequests | int | `1024` | Maximum number of requests on Envoy clusters | | envoy.connectTimeoutSeconds | int | `2` | Time in seconds after which a TCP connection attempt times out | | envoy.debug.admin.enabled | bool | `false` | Enable admin interface for cilium-envoy. This is useful for debugging and should not be enabled in production. | | envoy.debug.admin.port | int | `9901` | Port number (bound to loopback interface). kubectl port-forward can be used to access the admin interface. | @@ -394,7 +470,8 @@ contributors across the globe, there is almost always someone available to help. | envoy.httpRetryCount | int | `3` | Maximum number of retries for each HTTP request | | envoy.httpUpstreamLingerTimeout | string | `nil` | Time in seconds to block Envoy worker thread while an upstream HTTP connection is closing. If set to 0, the connection is closed immediately (with TCP RST). If set to -1, the connection is closed asynchronously in the background. | | envoy.idleTimeoutDurationSeconds | int | `60` | Set Envoy upstream HTTP idle connection timeout seconds. Does not apply to connections with pending requests. Default 60s | -| envoy.image | object | `{"digest":"sha256:81398e449f2d3d0a6a70527e4f641aaa685d3156bea0bb30712fae3fd8822b86","override":null,"pullPolicy":"IfNotPresent","repository":"quay.io/cilium/cilium-envoy","tag":"v1.35.9-1767794330-db497dd19e346b39d81d7b5c0dedf6c812bcc5c9","useDigest":true}` | Envoy container image. | +| envoy.image | object | `{"digest":"sha256:ba0ab8adac082d50d525fd2c5ba096c8facea3a471561b7c61c7a5b9c2e0de0d","override":null,"pullPolicy":"IfNotPresent","repository":"quay.io/cilium/cilium-envoy","tag":"v1.36.6-1776000132-2437d2edeaf4d9b56ef279bd0d71127440c067aa","useDigest":true}` | Envoy container image. | +| envoy.initContainers | list | `[]` | Init containers added to the cilium Envoy DaemonSet. | | envoy.initialFetchTimeoutSeconds | int | `30` | Time in seconds after which the initial fetch on an xDS stream is considered timed out | | envoy.livenessProbe.enabled | bool | `true` | Enable liveness probe for cilium-envoy | | envoy.livenessProbe.failureThreshold | int | `10` | failure threshold of liveness probe | @@ -406,6 +483,7 @@ contributors across the globe, there is almost always someone available to help. | envoy.log.path | string | `""` | Path to a separate Envoy log file, if any. Defaults to /dev/stdout. | | envoy.maxConcurrentRetries | int | `128` | Maximum number of concurrent retries on Envoy clusters | | envoy.maxConnectionDurationSeconds | int | `0` | Set Envoy HTTP option max_connection_duration seconds. Default 0 (disable) | +| envoy.maxGlobalDownstreamConnections | int | `50000` | Maximum number of global downstream connections | | envoy.maxRequestsPerConnection | int | `0` | ProxyMaxRequestsPerConnection specifies the max_requests_per_connection setting for Envoy | | envoy.nodeSelector | object | `{"kubernetes.io/os":"linux"}` | Node selector for cilium-envoy. | | envoy.podAnnotations | object | `{}` | Annotations to be added to envoy pods | @@ -439,6 +517,7 @@ contributors across the globe, there is almost always someone available to help. | envoy.terminationGracePeriodSeconds | int | `1` | Configure termination grace period for cilium-envoy DaemonSet. | | envoy.tolerations | list | `[{"operator":"Exists"}]` | Node tolerations for envoy scheduling to nodes with taints ref: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ | | envoy.updateStrategy | object | `{"rollingUpdate":{"maxUnavailable":2},"type":"RollingUpdate"}` | cilium-envoy update strategy ref: https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/#updating-a-daemonset | +| envoy.useOriginalSourceAddress | bool | `true` | For cases when CiliumEnvoyConfig is not used directly (Ingress, Gateway), configures Cilium BPF Metadata listener filter to use the original source address when extracting the metadata for a request. | | envoy.xffNumTrustedHopsL7PolicyEgress | int | `0` | Number of trusted hops regarding the x-forwarded-for and related HTTP headers for the egress L7 policy enforcement Envoy listeners. | | envoy.xffNumTrustedHopsL7PolicyIngress | int | `0` | Number of trusted hops regarding the x-forwarded-for and related HTTP headers for the ingress L7 policy enforcement Envoy listeners. | | envoyConfig.enabled | bool | `false` | Enable CiliumEnvoyConfig CRD CiliumEnvoyConfig CRD can also be implicitly enabled by other options. | @@ -482,12 +561,13 @@ contributors across the globe, there is almost always someone available to help. | hubble.dropEventEmitter.interval | string | `"2m"` | - Minimum time between emitting same events. | | hubble.dropEventEmitter.reasons | list | `["auth_required","policy_denied"]` | - Drop reasons to emit events for. ref: https://docs.cilium.io/en/stable/_api/v1/flow/README/#dropreason | | hubble.enabled | bool | `true` | Enable Hubble (true by default). | -| hubble.export | object | `{"dynamic":{"config":{"configMapName":"cilium-flowlog-config","content":[{"excludeFilters":[],"fieldMask":[],"fileCompress":false,"fileMaxBackups":5,"fileMaxSizeMb":10,"filePath":"/var/run/cilium/hubble/events.log","includeFilters":[],"name":"all"}],"createConfigMap":true},"enabled":false},"static":{"allowList":[],"denyList":[],"enabled":false,"fieldMask":[],"fileCompress":false,"fileMaxBackups":5,"fileMaxSizeMb":10,"filePath":"/var/run/cilium/hubble/events.log"}}` | Hubble flows export. | -| hubble.export.dynamic | object | `{"config":{"configMapName":"cilium-flowlog-config","content":[{"excludeFilters":[],"fieldMask":[],"fileCompress":false,"fileMaxBackups":5,"fileMaxSizeMb":10,"filePath":"/var/run/cilium/hubble/events.log","includeFilters":[],"name":"all"}],"createConfigMap":true},"enabled":false}` | - Dynamic exporters configuration. Dynamic exporters may be reconfigured without a need of agent restarts. | +| hubble.export | object | `{"dynamic":{"config":{"configMapName":"cilium-flowlog-config","content":[{"aggregationInterval":"0s","excludeFilters":[],"fieldAggregate":[],"fieldMask":[],"fileCompress":false,"fileMaxBackups":5,"fileMaxSizeMb":10,"filePath":"/var/run/cilium/hubble/events.log","includeFilters":[],"name":"all"}],"createConfigMap":true},"enabled":false},"static":{"aggregationInterval":"0s","allowList":[],"denyList":[],"enabled":false,"fieldAggregate":[],"fieldMask":[],"fileCompress":false,"fileMaxBackups":5,"fileMaxSizeMb":10,"filePath":"/var/run/cilium/hubble/events.log"}}` | Hubble flows export. | +| hubble.export.dynamic | object | `{"config":{"configMapName":"cilium-flowlog-config","content":[{"aggregationInterval":"0s","excludeFilters":[],"fieldAggregate":[],"fieldMask":[],"fileCompress":false,"fileMaxBackups":5,"fileMaxSizeMb":10,"filePath":"/var/run/cilium/hubble/events.log","includeFilters":[],"name":"all"}],"createConfigMap":true},"enabled":false}` | - Dynamic exporters configuration. Dynamic exporters may be reconfigured without a need of agent restarts. | | hubble.export.dynamic.config.configMapName | string | `"cilium-flowlog-config"` | -- Name of configmap with configuration that may be altered to reconfigure exporters within a running agents. | -| hubble.export.dynamic.config.content | list | `[{"excludeFilters":[],"fieldMask":[],"fileCompress":false,"fileMaxBackups":5,"fileMaxSizeMb":10,"filePath":"/var/run/cilium/hubble/events.log","includeFilters":[],"name":"all"}]` | -- Exporters configuration in YAML format. | +| hubble.export.dynamic.config.content | list | `[{"aggregationInterval":"0s","excludeFilters":[],"fieldAggregate":[],"fieldMask":[],"fileCompress":false,"fileMaxBackups":5,"fileMaxSizeMb":10,"filePath":"/var/run/cilium/hubble/events.log","includeFilters":[],"name":"all"}]` | -- Exporters configuration in YAML format. | | hubble.export.dynamic.config.createConfigMap | bool | `true` | -- True if helm installer should create config map. Switch to false if you want to self maintain the file content. | -| hubble.export.static | object | `{"allowList":[],"denyList":[],"enabled":false,"fieldMask":[],"fileCompress":false,"fileMaxBackups":5,"fileMaxSizeMb":10,"filePath":"/var/run/cilium/hubble/events.log"}` | - Static exporter configuration. Static exporter is bound to agent lifecycle. | +| hubble.export.static | object | `{"aggregationInterval":"0s","allowList":[],"denyList":[],"enabled":false,"fieldAggregate":[],"fieldMask":[],"fileCompress":false,"fileMaxBackups":5,"fileMaxSizeMb":10,"filePath":"/var/run/cilium/hubble/events.log"}` | - Static exporter configuration. Static exporter is bound to agent lifecycle. | +| hubble.export.static.aggregationInterval | string | `"0s"` | - Defines the interval at which to aggregate before exporting Hubble flows. Aggregation feature is only enabled when fieldAggregate is specified and aggregationInterval > 0s. | | hubble.export.static.fileCompress | bool | `false` | - Enable compression of rotated files. | | hubble.export.static.fileMaxBackups | int | `5` | - Defines max number of backup/rotated files. | | hubble.export.static.fileMaxSizeMb | int | `10` | - Defines max file size of output file before it gets rotated. | @@ -535,9 +615,12 @@ contributors across the globe, there is almost always someone available to help. | hubble.relay.extraVolumes | list | `[]` | Additional hubble-relay volumes. | | hubble.relay.gops.enabled | bool | `true` | Enable gops for hubble-relay | | hubble.relay.gops.port | int | `9893` | Configure gops listen port for hubble-relay | -| hubble.relay.image | object | `{"digest":"sha256:fb6135e34c31e5f175cb5e75f86cea52ef2ff12b49bcefb7088ed93f5009eb8e","override":null,"pullPolicy":"IfNotPresent","repository":"quay.io/cilium/hubble-relay","tag":"v1.18.6","useDigest":true}` | Hubble-relay container image. | +| hubble.relay.image | object | `{"digest":"sha256:5ee21d57b6ef2aa6db67e603a735fdceb162454b352b7335b651456e308f681b","override":null,"pullPolicy":"IfNotPresent","repository":"quay.io/cilium/hubble-relay","tag":"v1.19.3","useDigest":true}` | Hubble-relay container image. | | hubble.relay.listenHost | string | `""` | Host to listen to. Specify an empty string to bind to all the interfaces. | | hubble.relay.listenPort | string | `"4245"` | Port to listen to. | +| hubble.relay.logOptions | object | `{"format":null,"level":null}` | Logging configuration for hubble-relay. | +| hubble.relay.logOptions.format | string | text-ts | Log format for hubble-relay. Valid values are: text, text-ts, json, json-ts. | +| hubble.relay.logOptions.level | string | info | Log level for hubble-relay. Valid values are: debug, info, warn, error. | | hubble.relay.nodeSelector | object | `{"kubernetes.io/os":"linux"}` | Node labels for pod assignment ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector | | hubble.relay.podAnnotations | object | `{}` | Annotations to be added to hubble-relay pods | | hubble.relay.podDisruptionBudget.enabled | bool | `false` | enable PodDisruptionBudget ref: https://kubernetes.io/docs/concepts/workloads/pods/disruptions/ | @@ -547,7 +630,9 @@ contributors across the globe, there is almost always someone available to help. | hubble.relay.podLabels | object | `{}` | Labels to be added to hubble-relay pods | | hubble.relay.podSecurityContext | object | `{"fsGroup":65532,"seccompProfile":{"type":"RuntimeDefault"}}` | hubble-relay pod security context | | hubble.relay.pprof.address | string | `"localhost"` | Configure pprof listen address for hubble-relay | +| hubble.relay.pprof.blockProfileRate | int | `0` | Enable goroutine blocking profiling for hubble-relay and set the rate of sampled events in nanoseconds (set to 1 to sample all events [warning: performance overhead]) | | hubble.relay.pprof.enabled | bool | `false` | Enable pprof for hubble-relay | +| hubble.relay.pprof.mutexProfileFraction | int | `0` | Enable mutex contention profiling for hubble-relay and set the fraction of sampled events (set to 1 to sample all events) | | hubble.relay.pprof.port | int | `6062` | Configure pprof listen port for hubble-relay | | hubble.relay.priorityClassName | string | `""` | The priority class to use for hubble-relay | | hubble.relay.prometheus | object | `{"enabled":false,"port":9966,"serviceMonitor":{"annotations":{},"enabled":false,"interval":"10s","labels":{},"metricRelabelings":null,"relabelings":null,"scrapeTimeout":null}}` | Enable prometheus metrics for hubble-relay on the configured port at /metrics | @@ -641,13 +726,14 @@ contributors across the globe, there is almost always someone available to help. | hubble.ui.tls.client.cert | string | `""` | base64 encoded PEM values for the Hubble UI client certificate (deprecated). Use existingSecret instead. | | hubble.ui.tls.client.existingSecret | string | `""` | Name of the Secret containing the client certificate and key for Hubble UI If specified, cert and key are ignored. | | hubble.ui.tls.client.key | string | `""` | base64 encoded PEM values for the Hubble UI client key (deprecated). Use existingSecret instead. | +| hubble.ui.tmpVolume | object | `{}` | Configure temporary volume for hubble-ui | | hubble.ui.tolerations | list | `[]` | Node tolerations for pod assignment on nodes with taints ref: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ | | hubble.ui.topologySpreadConstraints | list | `[]` | Pod topology spread constraints for hubble-ui | | hubble.ui.updateStrategy | object | `{"rollingUpdate":{"maxUnavailable":1},"type":"RollingUpdate"}` | hubble-ui update strategy. | | identityAllocationMode | string | `"crd"` | Method to use for identity allocation (`crd`, `kvstore` or `doublewrite-readkvstore` / `doublewrite-readcrd` for migrating between identity backends). | | identityChangeGracePeriod | string | `"5s"` | Time to wait before using new identity on endpoint identity change. | | identityManagementMode | string | `"agent"` | Control whether CiliumIdentities are created by the agent ("agent"), the operator ("operator") or both ("both"). "Both" should be used only to migrate between "agent" and "operator". Operator-managed identities is a beta feature. | -| image | object | `{"digest":"sha256:42ec562a5ff6c8a860c0639f5a7611685e253fd9eb2d2fcdade693724c9166a4","override":null,"pullPolicy":"IfNotPresent","repository":"quay.io/cilium/cilium","tag":"v1.18.6","useDigest":true}` | Agent container image. | +| image | object | `{"digest":"sha256:2e61680593cddca8b6c055f6d4c849d87a26a1c91c7e3b8b56c7fb76ab7b7b10","override":null,"pullPolicy":"IfNotPresent","repository":"quay.io/cilium/cilium","tag":"v1.19.3","useDigest":true}` | Agent container image. | | imagePullSecrets | list | `[]` | Configure image pull secrets for pulling container images | | ingressController.default | bool | `false` | Set cilium ingress controller to be the default ingress controller This will let cilium ingress controller route entries without ingress class set | | ingressController.defaultSecretName | string | `nil` | Default secret name for ingresses without .spec.tls[].secretName set. | @@ -682,6 +768,11 @@ contributors across the globe, there is almost always someone available to help. | ipam.installUplinkRoutesForDelegatedIPAM | bool | `false` | Install ingress/egress routes through uplink on host for Pods when working with delegated IPAM plugin. | | ipam.mode | string | `"cluster-pool"` | Configure IP Address Management mode. ref: https://docs.cilium.io/en/stable/network/concepts/ipam/ | | ipam.multiPoolPreAllocation | string | `""` | Pre-allocation settings for IPAM in Multi-Pool mode | +| ipam.nodeSpec | object | `{"ipamMaxAllocate":null,"ipamMinAllocate":null,"ipamPreAllocate":null,"ipamStaticIPTags":[]}` | NodeSpec configuration for the IPAM | +| ipam.nodeSpec.ipamMaxAllocate | string | `nil` | IPAM max allocate @schema type: [null, integer] @schema | +| ipam.nodeSpec.ipamMinAllocate | string | `nil` | IPAM min allocate @schema type: [null, integer] @schema | +| ipam.nodeSpec.ipamPreAllocate | string | `nil` | IPAM pre allocate @schema type: [null, integer] @schema | +| ipam.nodeSpec.ipamStaticIPTags | list | `[]` | IPAM static IP tags (currently only works with AWS and Azure) | | ipam.operator.autoCreateCiliumPodIPPools | object | `{}` | IP pools to auto-create in multi-pool IPAM mode. | | ipam.operator.clusterPoolIPv4MaskSize | int | `24` | IPv4 CIDR mask size to delegate to individual nodes for IPAM. | | ipam.operator.clusterPoolIPv4PodCIDRList | list | `["10.0.0.0/8"]` | IPv4 CIDR list range to delegate to individual nodes for IPAM. | @@ -730,12 +821,13 @@ contributors across the globe, there is almost always someone available to help. | livenessProbe.failureThreshold | int | `10` | failure threshold of liveness probe | | livenessProbe.periodSeconds | int | `30` | interval between checks of the liveness probe | | livenessProbe.requireK8sConnectivity | bool | `false` | whether to require k8s connectivity as part of the check. | -| loadBalancer | object | `{"acceleration":"disabled","l7":{"algorithm":"round_robin","backend":"disabled","ports":[]}}` | Configure service load balancing | +| loadBalancer | object | `{"acceleration":"disabled","l7":{"algorithm":"round_robin","backend":"disabled","ports":[]},"serviceTopology":false}` | Configure service load balancing | | loadBalancer.acceleration | string | `"disabled"` | acceleration is the option to accelerate service handling via XDP Applicable values can be: disabled (do not use XDP), native (XDP BPF program is run directly out of the networking driver's early receive path), or best-effort (use native mode XDP acceleration on devices that support it). | | loadBalancer.l7 | object | `{"algorithm":"round_robin","backend":"disabled","ports":[]}` | L7 LoadBalancer | | loadBalancer.l7.algorithm | string | `"round_robin"` | Default LB algorithm The default LB algorithm to be used for services, which can be overridden by the service annotation (e.g. service.cilium.io/lb-l7-algorithm) Applicable values: round_robin, least_request, random | | loadBalancer.l7.backend | string | `"disabled"` | Enable L7 service load balancing via envoy proxy. The request to a k8s service, which has specific annotation e.g. service.cilium.io/lb-l7, will be forwarded to the local backend proxy to be load balanced to the service endpoints. Please refer to docs for supported annotations for more configuration. Applicable values: - envoy: Enable L7 load balancing via envoy proxy. This will automatically set enable-envoy-config as well. - disabled: Disable L7 load balancing by way of service annotation. | | loadBalancer.l7.ports | list | `[]` | List of ports from service to be automatically redirected to above backend. Any service exposing one of these ports will be automatically redirected. Fine-grained control can be achieved by using the service annotation. | +| loadBalancer.serviceTopology | bool | `false` | serviceTopology enables K8s Topology Aware Hints -based service endpoints filtering | | localRedirectPolicies.addressMatcherCIDRs | string | `nil` | Limit the allowed addresses in Address Matcher rule of Local Redirect Policies to the given CIDRs. @schema@ type: [null, array] @schema@ | | localRedirectPolicies.enabled | bool | `false` | Enable local redirect policies. | | localRedirectPolicy | bool | `false` | Enable Local Redirect Policy (deprecated, please use 'localRedirectPolicies.enabled' instead) | @@ -744,19 +836,18 @@ contributors across the globe, there is almost always someone available to help. | monitor | object | `{"enabled":false}` | cilium-monitor sidecar. | | monitor.enabled | bool | `false` | Enable the cilium-monitor sidecar. | | name | string | `"cilium"` | Agent daemonset name. | -| namespaceOverride | string | `""` | namespaceOverride allows to override the destination namespace for Cilium resources. This property allows to use Cilium as part of an Umbrella Chart with different targets. | +| namespaceOverride | string | `""` | namespaceOverride allows to override the destination namespace for Cilium resources. | | nat.mapStatsEntries | int | `32` | Number of the top-k SNAT map connections to track in Cilium statedb. | | nat.mapStatsInterval | string | `"30s"` | Interval between how often SNAT map is counted for stats. | | nat46x64Gateway | object | `{"enabled":false}` | Configure standalone NAT46/NAT64 gateway | | nat46x64Gateway.enabled | bool | `false` | Enable RFC6052-prefixed translation | | nodeIPAM.enabled | bool | `false` | Configure Node IPAM ref: https://docs.cilium.io/en/stable/network/node-ipam/ | -| nodePort | object | `{"addresses":null,"autoProtectPortRange":true,"bindProtection":true,"enableHealthCheck":true,"enableHealthCheckLoadBalancerIP":false,"enabled":false}` | Configure N-S k8s service loadbalancing | +| nodePort | object | `{"addresses":null,"autoProtectPortRange":true,"bindProtection":true,"enableHealthCheck":true,"enableHealthCheckLoadBalancerIP":false}` | Configure N-S k8s service loadbalancing | | nodePort.addresses | string | `nil` | List of CIDRs for choosing which IP addresses assigned to native devices are used for NodePort load-balancing. By default this is empty and the first suitable, preferably private, IPv4 and IPv6 address assigned to each device is used. Example: addresses: ["192.168.1.0/24", "2001::/64"] | | nodePort.autoProtectPortRange | bool | `true` | Append NodePort range to ip_local_reserved_ports if clash with ephemeral ports is detected. | | nodePort.bindProtection | bool | `true` | Set to true to prevent applications binding to service ports. | | nodePort.enableHealthCheck | bool | `true` | Enable healthcheck nodePort server for NodePort services | | nodePort.enableHealthCheckLoadBalancerIP | bool | `false` | Enable access of the healthcheck nodePort on the LoadBalancerIP. Needs EnableHealthCheck to be enabled | -| nodePort.enabled | bool | `false` | Enable the Cilium NodePort service implementation. | | nodeSelector | object | `{"kubernetes.io/os":"linux"}` | Node selector for cilium-agent. | | nodeSelectorLabels | bool | `false` | Enable/Disable use of node label based identity | | nodeinit.affinity | object | `{}` | Affinity for cilium-nodeinit | @@ -766,7 +857,7 @@ contributors across the globe, there is almost always someone available to help. | nodeinit.extraEnv | list | `[]` | Additional nodeinit environment variables. | | nodeinit.extraVolumeMounts | list | `[]` | Additional nodeinit volumeMounts. | | nodeinit.extraVolumes | list | `[]` | Additional nodeinit volumes. | -| nodeinit.image | object | `{"digest":"sha256:5bdca3c2dec2c79f58d45a7a560bf1098c2126350c901379fe850b7f78d3d757","override":null,"pullPolicy":"IfNotPresent","repository":"quay.io/cilium/startup-script","tag":"1755531540-60ee83e","useDigest":true}` | node-init image. | +| nodeinit.image | object | `{"digest":"sha256:50b9cf9c280096b59b80d2fc8ee6638facef79ac18998a22f0cbc40d5d28c16f","override":null,"pullPolicy":"IfNotPresent","repository":"quay.io/cilium/startup-script","tag":"1763560095-8f36c34","useDigest":true}` | node-init image. | | nodeinit.nodeSelector | object | `{"kubernetes.io/os":"linux"}` | Node labels for nodeinit pod assignment ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector | | nodeinit.podAnnotations | object | `{}` | Annotations to be added to node-init pods. | | nodeinit.podLabels | object | `{}` | Labels to be added to node-init pods. | @@ -779,6 +870,7 @@ contributors across the globe, there is almost always someone available to help. | nodeinit.startup | object | `{"postScript":"","preScript":""}` | startup offers way to customize startup nodeinit script (pre and post position) | | nodeinit.tolerations | list | `[{"operator":"Exists"}]` | Node tolerations for nodeinit scheduling to nodes with taints ref: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ | | nodeinit.updateStrategy | object | `{"type":"RollingUpdate"}` | node-init update strategy | +| nodeinit.waitForCloudInit | bool | `false` | wait for Cloud init to finish on the host and assume the node has cloud init installed | | operator.affinity | object | `{"podAntiAffinity":{"requiredDuringSchedulingIgnoredDuringExecution":[{"labelSelector":{"matchLabels":{"io.cilium/app":"operator"}},"topologyKey":"kubernetes.io/hostname"}]}}` | Affinity for cilium-operator | | operator.annotations | object | `{}` | Annotations to be added to all top-level cilium-operator objects (resources under templates/cilium-operator) | | operator.dashboards | object | `{"annotations":{},"enabled":false,"label":"grafana_dashboard","labelValue":"1","namespace":null}` | Grafana dashboards for cilium-operator grafana can import dashboards based on the label and value ref: https://github.com/grafana/helm-charts/tree/main/charts/grafana#sidecar-for-dashboards | @@ -793,7 +885,7 @@ contributors across the globe, there is almost always someone available to help. | operator.hostNetwork | bool | `true` | HostNetwork setting | | operator.identityGCInterval | string | `"15m0s"` | Interval for identity garbage collection. | | operator.identityHeartbeatTimeout | string | `"30m0s"` | Timeout for identity heartbeats. | -| operator.image | object | `{"alibabacloudDigest":"sha256:212c4cbe27da3772bcb952b8f8cbaa0b0eef72488b52edf90ad2b32072a3ca4c","awsDigest":"sha256:47dbc1a5bd483fec170dab7fb0bf2cca3585a4893675b0324d41d97bac8be5eb","azureDigest":"sha256:a57aff47aeb32eccfedaa2a49d1af984d996d6d6de79609c232e0c4cf9ce97a1","genericDigest":"sha256:34a827ce9ed021c8adf8f0feca131f53b3c54a3ef529053d871d0347ec4d69af","override":null,"pullPolicy":"IfNotPresent","repository":"quay.io/cilium/operator","suffix":"","tag":"v1.18.6","useDigest":true}` | cilium-operator image. | +| operator.image | object | `{"alibabacloudDigest":"sha256:176321a65123373ff8c7823b25183102cbad98375e8d6c80b96d68b6e8491103","awsDigest":"sha256:a53dcbfb77282bf2ddd3abbe60f6d49762e7c1389a36cb35b71d504644a56640","azureDigest":"sha256:699c1571a3df1a98882ee13610d47cffb7b34ee7e8d276096db798a5f6c7e4cb","genericDigest":"sha256:205b09b0ed6accbf9fe688d312a9f0fcfc6a316fc081c23fbffb472af5dd62cd","override":null,"pullPolicy":"IfNotPresent","repository":"quay.io/cilium/operator","suffix":"","tag":"v1.19.3","useDigest":true}` | cilium-operator image. | | operator.nodeGCInterval | string | `"5m0s"` | Interval for cilium node garbage collection. | | operator.nodeSelector | object | `{"kubernetes.io/os":"linux"}` | Node labels for cilium-operator pod assignment ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector | | operator.podAnnotations | object | `{}` | Annotations to be added to cilium-operator pods | @@ -804,10 +896,12 @@ contributors across the globe, there is almost always someone available to help. | operator.podLabels | object | `{}` | Labels to be added to cilium-operator pods | | operator.podSecurityContext | object | `{"seccompProfile":{"type":"RuntimeDefault"}}` | Security context to be added to cilium-operator pods | | operator.pprof.address | string | `"localhost"` | Configure pprof listen address for cilium-operator | +| operator.pprof.blockProfileRate | int | `0` | Enable goroutine blocking profiling for cilium-operator and set the rate of sampled events in nanoseconds (set to 1 to sample all events [warning: performance overhead]) | | operator.pprof.enabled | bool | `false` | Enable pprof for cilium-operator | +| operator.pprof.mutexProfileFraction | int | `0` | Enable mutex contention profiling for cilium-operator and set the fraction of sampled events (set to 1 to sample all events) | | operator.pprof.port | int | `6061` | Configure pprof listen port for cilium-operator | | operator.priorityClassName | string | `""` | The priority class to use for cilium-operator | -| operator.prometheus | object | `{"enabled":true,"metricsService":false,"port":9963,"serviceMonitor":{"annotations":{},"enabled":false,"interval":"10s","jobLabel":"","labels":{},"metricRelabelings":null,"relabelings":null,"scrapeTimeout":null}}` | Enable prometheus metrics for cilium-operator on the configured port at /metrics | +| operator.prometheus | object | `{"enabled":true,"metricsService":false,"port":9963,"serviceMonitor":{"annotations":{},"enabled":false,"interval":"10s","jobLabel":"","labels":{},"metricRelabelings":null,"relabelings":null,"scrapeTimeout":null},"tls":{"enabled":false,"server":{"existingSecret":"","mtls":{"enabled":false}}}}` | Enable prometheus metrics for cilium-operator on the configured port at /metrics | | operator.prometheus.serviceMonitor.annotations | object | `{}` | Annotations to add to ServiceMonitor cilium-operator | | operator.prometheus.serviceMonitor.enabled | bool | `false` | Enable service monitors. This requires the prometheus CRDs to be available (see https://github.com/prometheus-operator/prometheus-operator/blob/main/example/prometheus-operator-crd/monitoring.coreos.com_servicemonitors.yaml) | | operator.prometheus.serviceMonitor.interval | string | `"10s"` | Interval for scrape metrics. | @@ -816,6 +910,8 @@ contributors across the globe, there is almost always someone available to help. | operator.prometheus.serviceMonitor.metricRelabelings | string | `nil` | Metrics relabeling configs for the ServiceMonitor cilium-operator | | operator.prometheus.serviceMonitor.relabelings | string | `nil` | Relabeling configs for the ServiceMonitor cilium-operator | | operator.prometheus.serviceMonitor.scrapeTimeout | string | `nil` | Timeout after which scrape is considered to be failed. | +| operator.prometheus.tls | object | `{"enabled":false,"server":{"existingSecret":"","mtls":{"enabled":false}}}` | TLS configuration for Prometheus | +| operator.prometheus.tls.server.existingSecret | string | `""` | Name of the Secret containing the certificate, key and CA files for the Prometheus server. | | operator.removeNodeTaints | bool | `true` | Remove Cilium node taint from Kubernetes nodes that have a healthy Cilium pod running. | | operator.replicas | int | `2` | Number of replicas to run for the cilium-operator deployment | | operator.resources | object | `{}` | cilium-operator resource limits & requests ref: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | @@ -828,25 +924,30 @@ contributors across the globe, there is almost always someone available to help. | operator.topologySpreadConstraints | list | `[]` | Pod topology spread constraints for cilium-operator | | operator.unmanagedPodWatcher.intervalSeconds | int | `15` | Interval, in seconds, to check if there are any pods that are not managed by Cilium. | | operator.unmanagedPodWatcher.restart | bool | `true` | Restart any pod that are not managed by Cilium. | +| operator.unmanagedPodWatcher.selector | string | `nil` | Selector for pods that should be restarted when not managed by Cilium. If not set, defaults to built-in selector "k8s-app=kube-dns". Set to empty string to select all pods. @schema type: [null, string] @schema | | operator.updateStrategy | object | `{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"50%"},"type":"RollingUpdate"}` | cilium-operator update strategy | | pmtuDiscovery.enabled | bool | `false` | Enable path MTU discovery to send ICMP fragmentation-needed replies to the client. | +| pmtuDiscovery.packetizationLayerPMTUDMode | string | `"blackhole"` | Enable kernel probing path MTU discovery for Pods which uses different message sizes to search for correct MTU value. Valid values are: always, blackhole, disabled and unset (or empty). If value is 'unset' or left empty then will not try to override setting. | | podAnnotations | object | `{}` | Annotations to be added to agent pods | | podLabels | object | `{}` | Labels to be added to agent pods | | podSecurityContext | object | `{"appArmorProfile":{"type":"Unconfined"},"seccompProfile":{"type":"Unconfined"}}` | Security Context for cilium-agent pods. | | podSecurityContext.appArmorProfile | object | `{"type":"Unconfined"}` | AppArmorProfile options for the `cilium-agent` and init containers | | policyCIDRMatchMode | string | `nil` | policyCIDRMatchMode is a list of entities that may be selected by CIDR selector. The possible value is "nodes". | +| policyDenyResponse | string | `"none"` | Configure what the response should be to pod egress traffic denied by network policy. Possible values: - none (default) - icmp | | policyEnforcementMode | string | `"default"` | The agent can be put into one of the three policy enforcement modes: default, always and never. ref: https://docs.cilium.io/en/stable/security/policy/intro/#policy-enforcement-modes | | pprof.address | string | `"localhost"` | Configure pprof listen address for cilium-agent | +| pprof.blockProfileRate | int | `0` | Enable goroutine blocking profiling for cilium-agent and set the rate of sampled events in nanoseconds (set to 1 to sample all events [warning: performance overhead]) | | pprof.enabled | bool | `false` | Enable pprof for cilium-agent | +| pprof.mutexProfileFraction | int | `0` | Enable mutex contention profiling for cilium-agent and set the fraction of sampled events (set to 1 to sample all events) | | pprof.port | int | `6060` | Configure pprof listen port for cilium-agent | | preflight.affinity | object | `{"podAffinity":{"requiredDuringSchedulingIgnoredDuringExecution":[{"labelSelector":{"matchLabels":{"k8s-app":"cilium"}},"topologyKey":"kubernetes.io/hostname"}]}}` | Affinity for cilium-preflight | | preflight.annotations | object | `{}` | Annotations to be added to all top-level preflight objects (resources under templates/cilium-preflight) | | preflight.enabled | bool | `false` | Enable Cilium pre-flight resources (required for upgrade) | -| preflight.envoy.image | object | `{"digest":"sha256:81398e449f2d3d0a6a70527e4f641aaa685d3156bea0bb30712fae3fd8822b86","override":null,"pullPolicy":"IfNotPresent","repository":"quay.io/cilium/cilium-envoy","tag":"v1.35.9-1767794330-db497dd19e346b39d81d7b5c0dedf6c812bcc5c9","useDigest":true}` | Envoy pre-flight image. | +| preflight.envoy.image | object | `{"digest":"sha256:ba0ab8adac082d50d525fd2c5ba096c8facea3a471561b7c61c7a5b9c2e0de0d","override":null,"pullPolicy":"IfNotPresent","repository":"quay.io/cilium/cilium-envoy","tag":"v1.36.6-1776000132-2437d2edeaf4d9b56ef279bd0d71127440c067aa","useDigest":true}` | Envoy pre-flight image. | | preflight.extraEnv | list | `[]` | Additional preflight environment variables. | | preflight.extraVolumeMounts | list | `[]` | Additional preflight volumeMounts. | | preflight.extraVolumes | list | `[]` | Additional preflight volumes. | -| preflight.image | object | `{"digest":"sha256:42ec562a5ff6c8a860c0639f5a7611685e253fd9eb2d2fcdade693724c9166a4","override":null,"pullPolicy":"IfNotPresent","repository":"quay.io/cilium/cilium","tag":"v1.18.6","useDigest":true}` | Cilium pre-flight image. | +| preflight.image | object | `{"digest":"sha256:2e61680593cddca8b6c055f6d4c849d87a26a1c91c7e3b8b56c7fb76ab7b7b10","override":null,"pullPolicy":"IfNotPresent","repository":"quay.io/cilium/cilium","tag":"v1.19.3","useDigest":true}` | Cilium pre-flight image. | | preflight.nodeSelector | object | `{"kubernetes.io/os":"linux"}` | Node labels for preflight pod assignment ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector | | preflight.podAnnotations | object | `{}` | Annotations to be added to preflight pods | | preflight.podDisruptionBudget.enabled | bool | `false` | enable PodDisruptionBudget ref: https://kubernetes.io/docs/concepts/workloads/pods/disruptions/ | @@ -890,24 +991,37 @@ contributors across the globe, there is almost always someone available to help. | sctp | object | `{"enabled":false}` | SCTP Configuration Values | | sctp.enabled | bool | `false` | Enable SCTP support. NOTE: Currently, SCTP support does not support rewriting ports or multihoming. | | secretsNamespaceAnnotations | object | `{}` | Annotations to be added to all cilium-secret namespaces (resources under templates/cilium-secrets-namespace) | +| secretsNamespaceLabels | object | `{}` | Labels to be added to all cilium-secret namespaces (resources under templates/cilium-secrets-namespace) | | securityContext.allowPrivilegeEscalation | bool | `false` | disable privilege escalation | | securityContext.capabilities.applySysctlOverwrites | list | `["SYS_ADMIN","SYS_CHROOT","SYS_PTRACE"]` | capabilities for the `apply-sysctl-overwrites` init container | -| securityContext.capabilities.ciliumAgent | list | `["CHOWN","KILL","NET_ADMIN","NET_RAW","IPC_LOCK","SYS_MODULE","SYS_ADMIN","SYS_RESOURCE","DAC_OVERRIDE","FOWNER","SETGID","SETUID"]` | Capabilities for the `cilium-agent` container | +| securityContext.capabilities.ciliumAgent | list | `["CHOWN","KILL","NET_ADMIN","NET_RAW","IPC_LOCK","SYS_MODULE","SYS_ADMIN","SYS_RESOURCE","DAC_OVERRIDE","FOWNER","SETGID","SETUID","SYSLOG"]` | Capabilities for the `cilium-agent` container | | securityContext.capabilities.cleanCiliumState | list | `["NET_ADMIN","SYS_MODULE","SYS_ADMIN","SYS_RESOURCE"]` | Capabilities for the `clean-cilium-state` init container | | securityContext.capabilities.mountCgroup | list | `["SYS_ADMIN","SYS_CHROOT","SYS_PTRACE"]` | Capabilities for the `mount-cgroup` init container | | securityContext.privileged | bool | `false` | Run the pod with elevated privileges | | securityContext.seLinuxOptions | object | `{"level":"s0","type":"spc_t"}` | SELinux options for the `cilium-agent` and init containers | | serviceAccounts | object | Component's fully qualified name. | Define serviceAccount names for components. | | serviceAccounts.clustermeshcertgen | object | `{"annotations":{},"automount":true,"create":true,"name":"clustermesh-apiserver-generate-certs"}` | Clustermeshcertgen is used if clustermesh.apiserver.tls.auto.method=cronJob | +| serviceAccounts.corednsMCSAPI | object | `{"annotations":{},"automount":true,"create":true,"name":"cilium-coredns-mcsapi-autoconfig"}` | CorednsMCSAPI is used if clustermesh.mcsapi.corednsAutoConfigure.enabled=true | | serviceAccounts.hubblecertgen | object | `{"annotations":{},"automount":true,"create":true,"name":"hubble-generate-certs"}` | Hubblecertgen is used if hubble.tls.auto.method=cronJob | | serviceAccounts.nodeinit.enabled | bool | `false` | Enabled is temporary until https://github.com/cilium/cilium-cli/issues/1396 is implemented. Cilium CLI doesn't create the SAs for node-init, thus the workaround. Helm is not affected by this issue. Name and automount can be configured, if enabled is set to true. Otherwise, they are ignored. Enabled can be removed once the issue is fixed. Cilium-nodeinit DS must also be fixed. | +| serviceAccounts.ztunnel | object | `{"annotations":{},"automount":false,"create":true,"name":"ztunnel-cilium"}` | Ztunnel is used if encryption.type=ztunnel | | serviceNoBackendResponse | string | `"reject"` | Configure what the response should be to traffic for a service without backends. Possible values: - reject (default) - drop | | sleepAfterInit | bool | `false` | Do not run Cilium agent when running with clean mode. Useful to completely uninstall Cilium as it will stop Cilium from starting and create artifacts in the node. | | socketLB | object | `{"enabled":false}` | Configure socket LB | | socketLB.enabled | bool | `false` | Enable socket LB | +| standaloneDnsProxy | object | `{"annotations":{},"automountServiceAccountToken":false,"debug":false,"enabled":false,"image":{"digest":"","override":null,"pullPolicy":"IfNotPresent","repository":"","tag":"","useDigest":true},"nodeSelector":{"kubernetes.io/os":"linux"},"rollOutPods":false,"serverPort":10095,"tolerations":[],"updateStrategy":{"rollingUpdate":{"maxSurge":2,"maxUnavailable":0},"type":"RollingUpdate"}}` | Standalone DNS Proxy Configuration Note: The standalone DNS proxy uses the agent's dnsProxy.* configuration for DNS settings (proxyPort, enableDnsCompression) to ensure consistency. | +| standaloneDnsProxy.annotations | object | `{}` | Standalone DNS proxy annotations | +| standaloneDnsProxy.automountServiceAccountToken | bool | `false` | Standalone DNS proxy auto mount service account token | +| standaloneDnsProxy.debug | bool | `false` | Standalone DNS proxy debug mode | +| standaloneDnsProxy.enabled | bool | `false` | Enable standalone DNS proxy (alpha feature) | +| standaloneDnsProxy.image | object | `{"digest":"","override":null,"pullPolicy":"IfNotPresent","repository":"","tag":"","useDigest":true}` | Standalone DNS proxy image | +| standaloneDnsProxy.nodeSelector | object | `{"kubernetes.io/os":"linux"}` | Standalone DNS proxy Node Selector | +| standaloneDnsProxy.rollOutPods | bool | `false` | Roll out Standalone DNS proxy automatically when configmap is updated. | +| standaloneDnsProxy.serverPort | int | `10095` | Standalone DNS proxy server port | +| standaloneDnsProxy.tolerations | list | `[]` | Standalone DNS proxy tolerations | +| standaloneDnsProxy.updateStrategy | object | `{"rollingUpdate":{"maxSurge":2,"maxUnavailable":0},"type":"RollingUpdate"}` | Standalone DNS proxy update strategy | | startupProbe.failureThreshold | int | `300` | failure threshold of startup probe. Allow Cilium to take up to 600s to start up (300 attempts with 2s between attempts). | | startupProbe.periodSeconds | int | `2` | interval between checks of the startup probe | -| svcSourceRangeCheck | bool | `true` | Enable check of service source ranges (currently, only for LoadBalancer). | | synchronizeK8sNodes | bool | `true` | Synchronize Kubernetes nodes to kvstore and perform CNP GC. | | sysctlfix | object | `{"enabled":true}` | Configure sysctl override described in #20072. | | sysctlfix.enabled | bool | `true` | Enable the sysctl override. When enabled, the init container will mount the /proc of the host so that the `sysctlfix` utility can execute. | @@ -929,11 +1043,12 @@ contributors across the globe, there is almost always someone available to help. | tls.secretsNamespace | object | `{"create":true,"name":"cilium-secrets"}` | Configures where secrets used in CiliumNetworkPolicies will be looked for | | tls.secretsNamespace.create | bool | `true` | Create secrets namespace for TLS Interception secrets. | | tls.secretsNamespace.name | string | `"cilium-secrets"` | Name of TLS Interception secret namespace. | +| tmpVolume | object | `{}` | Configure temporary volume for cilium-agent | | tolerations | list | `[{"operator":"Exists"}]` | Node tolerations for agent scheduling to nodes with taints ref: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ | | tunnelPort | int | Port 8472 for VXLAN, Port 6081 for Geneve | Configure VXLAN and Geneve tunnel port. | | tunnelProtocol | string | `"vxlan"` | Tunneling protocol to use in tunneling mode and for ad-hoc tunnels. Possible values: - "" - vxlan - geneve | | tunnelSourcePortRange | string | 0-0 to let the kernel driver decide the range | Configure VXLAN and Geneve tunnel source port range hint. | -| underlayProtocol | string | `"ipv4"` | IP family for the underlay. | +| underlayProtocol | string | `"ipv4"` | IP family for the underlay. Possible values: - "ipv4" - "ipv6" | | updateStrategy | object | `{"rollingUpdate":{"maxUnavailable":2},"type":"RollingUpdate"}` | Cilium agent update strategy | | upgradeCompatibility | string | `nil` | upgradeCompatibility helps users upgrading to ensure that the configMap for Cilium will not change critical values to ensure continued operation This flag is not required for new installations. For example: '1.7', '1.8', '1.9' | | vtep.cidr | string | `""` | A space separated list of VTEP device CIDRs, for example "1.1.1.0/24 1.1.2.0/24" | diff --git a/packages/system/cilium/charts/cilium/files/cilium-envoy/configmap/bootstrap-config.yaml b/packages/system/cilium/charts/cilium/files/cilium-envoy/configmap/bootstrap-config.yaml index 52439049..90ebf4ac 100644 --- a/packages/system/cilium/charts/cilium/files/cilium-envoy/configmap/bootstrap-config.yaml +++ b/packages/system/cilium/charts/cilium/files/cilium-envoy/configmap/bootstrap-config.yaml @@ -167,6 +167,8 @@ staticResources: circuitBreakers: thresholds: - maxRetries: {{ .Values.envoy.maxConcurrentRetries }} + maxConnections: {{ .Values.envoy.clusterMaxConnections }} + maxRequests: {{ .Values.envoy.clusterMaxRequests }} lbPolicy: "CLUSTER_PROVIDED" typedExtensionProtocolOptions: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: @@ -183,6 +185,8 @@ staticResources: circuitBreakers: thresholds: - maxRetries: {{ .Values.envoy.maxConcurrentRetries }} + maxConnections: {{ .Values.envoy.clusterMaxConnections }} + maxRequests: {{ .Values.envoy.clusterMaxRequests }} lbPolicy: "CLUSTER_PROVIDED" typedExtensionProtocolOptions: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: @@ -204,6 +208,8 @@ staticResources: circuitBreakers: thresholds: - maxRetries: {{ .Values.envoy.maxConcurrentRetries }} + maxConnections: {{ .Values.envoy.clusterMaxConnections }} + maxRequests: {{ .Values.envoy.clusterMaxRequests }} lbPolicy: "CLUSTER_PROVIDED" typedExtensionProtocolOptions: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: @@ -220,6 +226,8 @@ staticResources: circuitBreakers: thresholds: - maxRetries: {{ .Values.envoy.maxConcurrentRetries }} + maxConnections: {{ .Values.envoy.clusterMaxConnections }} + maxRequests: {{ .Values.envoy.clusterMaxRequests }} lbPolicy: "CLUSTER_PROVIDED" typedExtensionProtocolOptions: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: @@ -292,7 +300,7 @@ overloadManager: - name: "envoy.resource_monitors.global_downstream_max_connections" typedConfig: "@type": "type.googleapis.com/envoy.extensions.resource_monitors.downstream_connections.v3.DownstreamConnectionsConfig" - max_active_downstream_connections: "50000" + max_active_downstream_connections: "{{ .Values.envoy.maxGlobalDownstreamConnections }}" applicationLogConfig: logFormat: {{- if .Values.envoy.log.format_json }} @@ -304,3 +312,4 @@ admin: address: pipe: path: "/var/run/cilium/envoy/sockets/admin.sock" + mode: 0660 diff --git a/packages/system/cilium/charts/cilium/files/nodeinit/startup.bash b/packages/system/cilium/charts/cilium/files/nodeinit/startup.bash index aa63cac8..68e6b50b 100644 --- a/packages/system/cilium/charts/cilium/files/nodeinit/startup.bash +++ b/packages/system/cilium/charts/cilium/files/nodeinit/startup.bash @@ -156,6 +156,14 @@ fi iptables -w -t nat -D POSTROUTING -m comment --comment "ip-masq: ensure nat POSTROUTING directs all non-LOCAL destination traffic to our custom IP-MASQ chain" -m addrtype ! --dst-type LOCAL -j IP-MASQ || true {{- end }} +{{- if .Values.nodeinit.waitForCloudInit }} +echo "Waiting for cloud-init..." +if command -v cloud-init >/dev/null 2>&1; then + cloud-init status --wait + echo "cloud-init completed!" +fi +{{- end }} + {{- if not (eq .Values.nodeinit.bootstrapFile "") }} mkdir -p {{ .Values.nodeinit.bootstrapFile | dir | quote }} date > {{ .Values.nodeinit.bootstrapFile | quote }} diff --git a/packages/system/cilium/charts/cilium/templates/_extensions.tpl b/packages/system/cilium/charts/cilium/templates/_extensions.tpl index bd8ba80b..8c0e5c20 100644 --- a/packages/system/cilium/charts/cilium/templates/_extensions.tpl +++ b/packages/system/cilium/charts/cilium/templates/_extensions.tpl @@ -21,6 +21,24 @@ dnsPolicy: {{ .Values.dnsPolicy }} {{- end }} {{- end }} +{{/* +Allow packagers to add extra volumes to cilium-operator. +*/}} +{{- define "cilium-operator.volumes.extra" }} +{{- end }} + +{{- define "cilium-operator.volumeMounts.extra" }} +{{- end }} + +{{/* +Allow packagers to set securityContext for cilium-operator. +*/}} +{{- define "cilium.operator.securityContext" }} +{{- with .Values.operator.securityContext }} +{{ toYaml . }} +{{- end }} +{{- end }} + {{/* Intentionally empty to allow downstream chart packagers to add extra containers to hubble-relay without having to modify the deployment manifest @@ -72,3 +90,87 @@ Allow packagers to add extra configuration to certgen. */}} {{- define "certgen.config.extra" -}} {{- end }} + +{{/* +Allow packagers to add extra arguments to the clustermesh-apiserver apiserver container. +*/}} +{{- define "clustermesh.apiserver.args.extra" -}} +{{- end }} + +{{/* +Allow packagers to add extra arguments to the clustermesh-apiserver kvstoremesh container. +*/}} +{{- define "clustermesh.kvstoremesh.args.extra" -}} +{{- end }} + +{{/* +Allow packagers to add init containers to the cilium-envoy pods. +*/}} +{{- define "envoy.initContainers" -}} +{{- end }} + +{{/* +Allow packagers to add extra args to the cilium-envoy container. +*/}} +{{- define "envoy.args.extra" -}} +{{- end }} + +{{/* +Allow packagers to add extra env vars to the cilium-envoy container. +*/}} +{{- define "envoy.env.extra" -}} +{{- end }} + +{{/* +Allow packagers to add extra volume mounts to the cilium-envoy container. +*/}} +{{- define "envoy.volumeMounts.extra" -}} +{{- end }} + +{{/* +Allow packagers to add extra host path mounts to the cilium-envoy container. +*/}} +{{- define "envoy.hostPathMounts.extra" -}} +{{- end }} + + +{{/* +Allow packagers to define set of ports for cilium-envoy container. +The template needs to allow overriding ports spec not just adding. +*/}} +{{- define "envoy.ports" -}} + {{- if .Values.envoy.prometheus.enabled }} + ports: + - name: envoy-metrics + containerPort: {{ .Values.envoy.prometheus.port }} + hostPort: {{ .Values.envoy.prometheus.port }} + protocol: TCP + {{- if and .Values.envoy.debug.admin.enabled .Values.envoy.debug.admin.port }} + - name: envoy-admin + containerPort: {{ .Values.envoy.debug.admin.port }} + hostPort: {{ .Values.envoy.debug.admin.port }} + protocol: TCP + {{- end }} + {{- end }} +{{- end }} + +{{/* +Allow packagers to define update strategy for cilium-envoy pods. +*/}} +{{- define "envoy.updateStrategy" -}} +{{- with .Values.envoy.updateStrategy }} +updateStrategy: + {{- toYaml . | trim | nindent 2 }} + {{- end }} +{{- end }} + +{{/* +Allow packagers to define affinity for cilium-envoy pods. +*/}} +{{- define "envoy.affinity" -}} +{{- with .Values.envoy.affinity }} +affinity: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- end }} + diff --git a/packages/system/cilium/charts/cilium/templates/_helpers.tpl b/packages/system/cilium/charts/cilium/templates/_helpers.tpl index ba91c353..e90aa482 100644 --- a/packages/system/cilium/charts/cilium/templates/_helpers.tpl +++ b/packages/system/cilium/charts/cilium/templates/_helpers.tpl @@ -131,12 +131,16 @@ To override the namespace and configMap when using `auto`: {{- define "k8sServiceHost" }} {{- $configmapName := default "cluster-info" .Values.k8sServiceLookupConfigMapName }} {{- $configmapNamespace := default "kube-public" .Values.k8sServiceLookupNamespace }} - {{- if and (eq .Values.k8sServiceHost "auto") (lookup "v1" "ConfigMap" $configmapNamespace $configmapName) }} + {{- if eq .Values.k8sServiceHost "auto" }} {{- $configmap := (lookup "v1" "ConfigMap" $configmapNamespace $configmapName) }} - {{- $kubeconfig := get $configmap.data "kubeconfig" }} - {{- $k8sServer := get ($kubeconfig | fromYaml) "clusters" | mustFirst | dig "cluster" "server" "" }} - {{- $uri := (split "https://" $k8sServer)._1 | trim }} - {{- (split ":" $uri)._0 | quote }} + {{- if $configmap }} + {{- $kubeconfig := get $configmap.data "kubeconfig" }} + {{- $k8sServer := get ($kubeconfig | fromYaml) "clusters" | mustFirst | dig "cluster" "server" "" }} + {{- $uri := (split "https://" $k8sServer)._1 | trim }} + {{- (split ":" $uri)._0 | quote }} + {{- else }} + {{- fail (printf "ConfigMap %s/%s not found, please create it or set k8sServiceHost to a valid value" $configmapNamespace $configmapName) }} + {{- end }} {{- else }} {{- .Values.k8sServiceHost | quote }} {{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/cilium-agent/clusterrole.yaml b/packages/system/cilium/charts/cilium/templates/cilium-agent/clusterrole.yaml index eaca204e..57b13447 100644 --- a/packages/system/cilium/charts/cilium/templates/cilium-agent/clusterrole.yaml +++ b/packages/system/cilium/charts/cilium/templates/cilium-agent/clusterrole.yaml @@ -94,7 +94,6 @@ rules: - cilium.io resources: - ciliumloadbalancerippools - - ciliumbgppeeringpolicies - ciliumbgpnodeconfigs - ciliumbgpadvertisements - ciliumbgppeerconfigs diff --git a/packages/system/cilium/charts/cilium/templates/cilium-agent/daemonset.yaml b/packages/system/cilium/charts/cilium/templates/cilium-agent/daemonset.yaml index c706da51..fff1b384 100644 --- a/packages/system/cilium/charts/cilium/templates/cilium-agent/daemonset.yaml +++ b/packages/system/cilium/charts/cilium/templates/cilium-agent/daemonset.yaml @@ -10,6 +10,7 @@ {{- $kubeProxyReplacement := (coalesce .Values.kubeProxyReplacement "false") -}} {{- $envoyDS := eq (include "envoyDaemonSetEnabled" .) "true" -}} +{{- $buildDaemonConfig := or (kindIs "invalid" .Values.daemon.configSources) (not (regexMatch "^config-map:[^,]+$" .Values.daemon.configSources)) -}} --- apiVersion: apps/v1 @@ -56,19 +57,6 @@ spec: # ensure pods roll when configmap updates cilium.io/cilium-configmap-checksum: {{ include (print $.Template.BasePath "/cilium-configmap.yaml") . | sha256sum | quote }} {{- end }} - {{- if not .Values.securityContext.privileged }} - {{- if semverCompare "<1.30.0" (printf "%d.%d.0" (semver .Capabilities.KubeVersion.Version).Major (semver .Capabilities.KubeVersion.Version).Minor) }} - # Set app AppArmor's profile to "unconfined". The value of this annotation - # can be modified as long users know which profiles they have available - # in AppArmor. - container.apparmor.security.beta.kubernetes.io/cilium-agent: "unconfined" - container.apparmor.security.beta.kubernetes.io/clean-cilium-state: "unconfined" - {{- if .Values.cgroup.autoMount.enabled }} - container.apparmor.security.beta.kubernetes.io/mount-cgroup: "unconfined" - container.apparmor.security.beta.kubernetes.io/apply-sysctl-overwrites: "unconfined" - {{- end }} - {{- end }} - {{- end }} {{- with .Values.podAnnotations }} {{- toYaml . | nindent 8 }} {{- end }} @@ -134,7 +122,7 @@ spec: httpGet: host: {{ .Values.ipv4.enabled | ternary "127.0.0.1" "::1" | quote }} path: /healthz - port: {{ .Values.healthPort }} + port: health scheme: HTTP httpHeaders: - name: "brief" @@ -154,7 +142,7 @@ spec: httpGet: host: {{ .Values.ipv4.enabled | ternary "127.0.0.1" "::1" | quote }} path: /healthz - port: {{ .Values.healthPort }} + port: health scheme: HTTP httpHeaders: - name: "brief" @@ -177,7 +165,7 @@ spec: httpGet: host: {{ .Values.ipv4.enabled | ternary "127.0.0.1" "::1" | quote }} path: /healthz - port: {{ .Values.healthPort }} + port: health scheme: HTTP httpHeaders: - name: "brief" @@ -250,12 +238,17 @@ spec: resources: {{- toYaml . | trim | nindent 10 }} {{- end }} - {{- if or .Values.prometheus.enabled (or .Values.hubble.metrics.enabled .Values.hubble.metrics.dynamic.enabled) }} ports: + - name: health + containerPort: {{ .Values.healthPort }} + hostPort: {{ .Values.healthPort }} + protocol: TCP + {{- if .Values.hubble.enabled }} - name: peer-service containerPort: {{ .Values.hubble.peerService.targetPort }} hostPort: {{ .Values.hubble.peerService.targetPort }} protocol: TCP + {{- end }} {{- if .Values.prometheus.enabled }} - name: prometheus containerPort: {{ .Values.prometheus.port }} @@ -280,7 +273,6 @@ spec: hostPort: {{ .Values.hubble.metrics.port }} protocol: TCP {{- end }} - {{- end }} securityContext: {{- if .Values.securityContext.privileged }} privileged: true @@ -375,6 +367,10 @@ spec: - name: cilium-ipsec-secrets mountPath: {{ .Values.encryption.ipsec.mountPath }} {{- end }} + {{- if and .Values.encryption.enabled (eq .Values.encryption.type "ztunnel") }} + - name: cilium-ztunnel-secrets + mountPath: /etc/ztunnel + {{- end }} {{- if .Values.kubeConfigPath }} - name: kube-config mountPath: {{ .Values.kubeConfigPath }} @@ -390,8 +386,14 @@ spec: mountPath: /var/lib/cilium/tls/hubble readOnly: true {{- end }} + {{- if $buildDaemonConfig }} - name: tmp mountPath: /tmp + {{- else }} + - name: cilium-config-path + mountPath: /tmp/cilium/config-map + readOnly: true + {{- end }} {{- range .Values.extraHostPathMounts }} - name: {{ .name }} mountPath: {{ .mountPath }} @@ -447,6 +449,7 @@ spec: {{- toYaml .Values.extraContainers | nindent 6 }} {{- end }} initContainers: + {{- if $buildDaemonConfig }} - name: config image: {{ include "cilium.image" .Values.image | quote }} imagePullPolicy: {{ .Values.image.pullPolicy }} @@ -513,6 +516,17 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} terminationMessagePolicy: FallbackToLogsOnError + securityContext: + {{- if .Values.securityContext.privileged }} + privileged: true + {{- else }} + capabilities: + add: + - NET_ADMIN + drop: + - ALL + {{- end}} + {{- end }} {{- if .Values.cgroup.autoMount.enabled }} # Required to mount cgroup2 filesystem on the underlying Kubernetes node. # We use nsenter command with host's cgroup and mount namespaces enabled. @@ -524,12 +538,15 @@ spec: value: {{ .Values.cgroup.hostRoot }} - name: BIN_PATH value: {{ .Values.cni.binPath }} - {{- with .Values.cgroup.autoMount.resources }} + {{- if .Values.cgroup.autoMount.resources }} resources: - {{- toYaml . | trim | nindent 10 }} + {{- toYaml .Values.cgroup.autoMount.resources | trim | nindent 10 }} + {{- else if .Values.initResources }} + resources: + {{- toYaml .Values.initResources | trim | nindent 10 }} {{- end }} command: - - sh + - bash - -ec # The statically linked Go program binary is invoked to avoid any # dependency on utilities like sh and mount that can be missing on certain @@ -575,7 +592,7 @@ spec: - name: BIN_PATH value: {{ .Values.cni.binPath }} command: - - sh + - bash - -ec # The statically linked Go program binary is invoked to avoid any # dependency on utilities like sh that can be missing on certain @@ -643,7 +660,7 @@ spec: {{- toYaml . | trim | nindent 10 }} {{- end }} command: - - sh + - bash - -c - | until test -s {{ (print "/tmp/cilium-bootstrap.d/" (.Values.nodeinit.bootstrapFile | base)) | quote }}; do @@ -821,7 +838,7 @@ spec: {{- end }} {{- if and .Values.clustermesh.config.enabled (not (and .Values.clustermesh.useAPIServer .Values.clustermesh.apiserver.kvstoremesh.enabled )) }} hostAliases: - {{- range $cluster := .Values.clustermesh.config.clusters }} + {{- range $_, $cluster := (include "clustermesh-clusters" . | fromJson) }} {{- range $ip := $cluster.ips }} - ip: {{ $ip }} hostnames: [ "{{ $cluster.name }}.{{ $.Values.clustermesh.config.domain }}" ] @@ -829,9 +846,20 @@ spec: {{- end }} {{- end }} volumes: - # For sharing configuration between the "config" initContainer and the agent + {{- if $buildDaemonConfig }} + # For sharing configuration between the "config" initContainer and the agent - name: tmp + {{- if .Values.tmpVolume }} + {{- toYaml .Values.tmpVolume | nindent 8 }} + {{- else }} emptyDir: {} + {{- end }} + {{- else }} + # To read the configuration from the config map + - name: cilium-config-path + configMap: + name: {{ trimPrefix "config-map:" .Values.daemon.configSources }} + {{- end }} # To keep state between restarts / upgrades - name: cilium-run hostPath: @@ -992,6 +1020,11 @@ spec: secret: secretName: {{ .Values.encryption.ipsec.secretName }} {{- end }} + {{- if and .Values.encryption.enabled (eq .Values.encryption.type "ztunnel") }} + - name: cilium-ztunnel-secrets + secret: + secretName: cilium-ztunnel-secrets + {{- end }} {{- if .Values.cni.configMap }} - name: cni-configuration configMap: diff --git a/packages/system/cilium/charts/cilium/templates/cilium-agent/role.yaml b/packages/system/cilium/charts/cilium/templates/cilium-agent/role.yaml index 89266e0d..df60d89b 100644 --- a/packages/system/cilium/charts/cilium/templates/cilium-agent/role.yaml +++ b/packages/system/cilium/charts/cilium/templates/cilium-agent/role.yaml @@ -120,7 +120,7 @@ rules: - watch {{- end}} -{{- if and .Values.operator.enabled .Values.serviceAccounts.operator.create $readSecretsOnlyFromSecretsNamespace .Values.tls.secretsNamespace.name }} +{{- if and .Values.agent (not .Values.preflight.enabled) .Values.serviceAccounts.cilium.create $readSecretsOnlyFromSecretsNamespace .Values.tls.secretsNamespace.name }} --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role diff --git a/packages/system/cilium/charts/cilium/templates/cilium-agent/rolebinding.yaml b/packages/system/cilium/charts/cilium/templates/cilium-agent/rolebinding.yaml index c76350b3..5caf2f15 100644 --- a/packages/system/cilium/charts/cilium/templates/cilium-agent/rolebinding.yaml +++ b/packages/system/cilium/charts/cilium/templates/cilium-agent/rolebinding.yaml @@ -126,7 +126,7 @@ subjects: namespace: {{ include "cilium.namespace" . }} {{- end}} -{{- if and (not .Values.preflight.enabled) $readSecretsOnlyFromSecretsNamespace .Values.tls.secretsNamespace.name }} +{{- if and .Values.agent (not .Values.preflight.enabled) .Values.serviceAccounts.cilium.create $readSecretsOnlyFromSecretsNamespace .Values.tls.secretsNamespace.name }} --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding diff --git a/packages/system/cilium/charts/cilium/templates/cilium-ca-secret.yaml b/packages/system/cilium/charts/cilium/templates/cilium-ca-secret.yaml index 7dd15d02..91569863 100644 --- a/packages/system/cilium/charts/cilium/templates/cilium-ca-secret.yaml +++ b/packages/system/cilium/charts/cilium/templates/cilium-ca-secret.yaml @@ -11,8 +11,13 @@ kind: Secret metadata: name: {{ .commonCASecretName }} namespace: {{ include "cilium.namespace" . }} - {{- with .Values.commonLabels }} labels: + {{- with .Values.commonLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} + cilium.io/helm-template-non-idempotent: "true" + {{- with .Values.nonIdempotentAnnotations }} + annotations: {{- toYaml . | nindent 4 }} {{- end }} data: diff --git a/packages/system/cilium/charts/cilium/templates/cilium-configmap.yaml b/packages/system/cilium/charts/cilium/templates/cilium-configmap.yaml index a9bb1cd9..5d76944d 100644 --- a/packages/system/cilium/charts/cilium/templates/cilium-configmap.yaml +++ b/packages/system/cilium/charts/cilium/templates/cilium-configmap.yaml @@ -33,12 +33,6 @@ {{- end }} {{- $defaultBpfCtTcpMax = 0 -}} {{- $defaultBpfCtAnyMax = 0 -}} - {{- $defaultKubeProxyReplacement = "probe" -}} -{{- end -}} - -{{- /* Default values when 1.9 was initially deployed */ -}} -{{- if semverCompare ">=1.9" (default "1.9" .Values.upgradeCompatibility) -}} - {{- $defaultKubeProxyReplacement = "probe" -}} {{- end -}} {{- /* Default values when 1.10 was initially deployed */ -}} @@ -52,7 +46,6 @@ {{- if .Values.azure.enabled }} {{- $azureUsePrimaryAddress = "false" -}} {{- end }} - {{- $defaultKubeProxyReplacement = "disabled" -}} {{- $defaultDNSProxyEnableTransparentMode = "true" -}} {{- end -}} @@ -71,6 +64,10 @@ {{- end }} {{- end -}} {{- $ipam := (coalesce .Values.ipam.mode $defaultIPAM) -}} +{{- if .Values.eni.enabled }} + {{- $ipam = "eni" -}} +{{- end }} + {{- $bpfCtTcpMax := (coalesce .Values.bpf.ctTcpMax $defaultBpfCtTcpMax) -}} {{- $bpfCtAnyMax := (coalesce .Values.bpf.ctAnyMax $defaultBpfCtAnyMax) -}} {{- $stringValueKPR := (toString .Values.kubeProxyReplacement) -}} @@ -258,6 +255,14 @@ data: operator-prometheus-serve-addr: ":{{ .Values.operator.prometheus.port }}" enable-metrics: "true" {{- end }} +{{- if and .Values.operator.prometheus.enabled .Values.operator.prometheus.tls.enabled }} + operator-prometheus-enable-tls: "true" + operator-prometheus-tls-cert-file: /var/lib/cilium/tls/prometheus/server.crt + operator-prometheus-tls-key-file: /var/lib/cilium/tls/prometheus/server.key + {{- if .Values.operator.prometheus.tls.server.mtls.enabled }} + operator-prometheus-tls-client-ca-files: /var/lib/cilium/tls/prometheus/client-ca.crt + {{- end }} +{{- end }} {{- if .Values.operator.skipCRDCreation }} skip-crd-creation: "true" @@ -456,6 +461,12 @@ data: # policy map (per endpoint) bpf-policy-map-max: "{{ .Values.bpf.policyMapMax | int }}" {{- end }} +{{- if has (kindOf .Values.bpf.policyMapPressureMetricsThreshold) (list "int64" "float64") }} + bpf-policy-map-pressure-metrics-threshold: {{ .Values.bpf.policyMapPressureMetricsThreshold | quote }} +{{- end }} +{{- if .Values.endpointPolicyUpdateTimeoutDuration }} + endpoint-policy-update-timeout: {{ .Values.endpointPolicyUpdateTimeoutDuration | quote }} +{{- end }} {{- if hasKey .Values.bpf "policyStatsMapMax" }} # bpf-policy-stats-map-max specifies the maximum number of entries in global # policy stats map @@ -505,7 +516,7 @@ data: cluster-name: {{ .Values.cluster.name | quote }} {{- if hasKey .Values.cluster "id" }} - # Unique ID of the cluster. Must be unique across all conneted clusters and + # Unique ID of the cluster. Must be unique across all connected clusters and # in the range of 1 and 255. Only relevant when building a mesh of clusters. cluster-id: "{{ .Values.cluster.id }}" {{- end }} @@ -526,7 +537,7 @@ data: {{- end }} {{- end }} - routing-mode: {{ .Values.routingMode | default (ternary "native" "tunnel" .Values.gke.enabled) | quote }} + routing-mode: {{ .Values.routingMode | default (ternary "native" "tunnel" (or .Values.eni.enabled .Values.gke.enabled)) | quote }} tunnel-protocol: {{ .Values.tunnelProtocol | default "vxlan" | quote }} {{- if eq .Values.routingMode "native" }} @@ -558,6 +569,10 @@ data: service-no-backend-response: "{{ .Values.serviceNoBackendResponse }}" {{- end}} +{{- if .Values.policyDenyResponse }} + policy-deny-response: "{{ .Values.policyDenyResponse }}" +{{- end}} + {{- if .Values.MTU }} mtu: {{ .Values.MTU | quote }} {{- end }} @@ -575,6 +590,35 @@ data: {{- end }} ec2-api-endpoint: {{ .Values.eni.ec2APIEndpoint | quote }} eni-tags: {{ .Values.eni.eniTags | toRawJson | quote }} +{{- if .Values.eni.nodeSpec }} + {{- if ne .Values.eni.nodeSpec.firstInterfaceIndex nil }} + eni-first-interface-index: {{ .Values.eni.nodeSpec.firstInterfaceIndex | quote }} + {{- end }} + {{- if .Values.eni.nodeSpec.subnetIDs }} + eni-subnet-ids: {{ .Values.eni.nodeSpec.subnetIDs | join "," | quote }} + {{- end }} + {{- if .Values.eni.nodeSpec.subnetTags }} + eni-subnet-tags: {{ .Values.eni.nodeSpec.subnetTags | join "," | quote }} + {{- end }} + {{- if .Values.eni.nodeSpec.securityGroups }} + eni-security-groups: {{ .Values.eni.nodeSpec.securityGroups | join "," | quote }} + {{- end }} + {{- if .Values.eni.nodeSpec.securityGroupTags }} + eni-security-group-tags: {{ .Values.eni.nodeSpec.securityGroupTags | join "," | quote }} + {{- end }} + {{- if .Values.eni.nodeSpec.excludeInterfaceTags }} + eni-exclude-interface-tags: {{ .Values.eni.nodeSpec.excludeInterfaceTags | join "," | quote }} + {{- end }} + {{- if .Values.eni.nodeSpec.usePrimaryAddress }} + eni-use-primary-address: "true" + {{- end }} + {{- if .Values.eni.nodeSpec.disablePrefixDelegation }} + eni-disable-prefix-delegation: "true" + {{- end }} + {{- if ne .Values.eni.nodeSpec.deleteOnTermination nil }} + eni-delete-on-termination: {{ .Values.eni.nodeSpec.deleteOnTermination | quote }} + {{- end }} +{{- end }} {{- if .Values.eni.subnetIDsFilter }} subnet-ids-filter: {{ .Values.eni.subnetIDsFilter | join " " | quote }} {{- end }} @@ -600,17 +644,39 @@ data: {{- end }} azure-use-primary-address: {{ $azureUsePrimaryAddress | quote }} {{- end }} +{{- if .Values.azure.nodeSpec.azureInterfaceName }} + azure-interface-name: {{ .Values.azure.nodeSpec.azureInterfaceName | quote }} +{{- end }} {{- if .Values.alibabacloud.enabled }} enable-endpoint-routes: "true" auto-create-cilium-node-resource: "true" {{- end }} +{{- if .Values.alibabacloud.nodeSpec.vSwitches }} + alibabacloud-vswitches: {{ .Values.alibabacloud.nodeSpec.vSwitches | join "," | quote }} +{{- end }} +{{- if .Values.alibabacloud.nodeSpec.vSwitchTags }} + alibabacloud-vswitch-tags: {{ .Values.alibabacloud.nodeSpec.vSwitchTags | join "," | quote }} +{{- end }} +{{- if .Values.alibabacloud.nodeSpec.securityGroups }} + alibabacloud-security-groups: {{ .Values.alibabacloud.nodeSpec.securityGroups | join "," | quote }} +{{- end }} +{{- if .Values.alibabacloud.nodeSpec.securityGroupTags }} + alibabacloud-security-group-tags: {{ .Values.alibabacloud.nodeSpec.securityGroupTags | join "," | quote }} +{{- end }} {{- if hasKey .Values "l7Proxy" }} # Enables L7 proxy for L7 policy enforcement and visibility enable-l7-proxy: {{ .Values.l7Proxy | quote }} {{- end }} +{{- if hasKey .Values "standaloneDnsProxy" }} + {{- if .Values.standaloneDnsProxy.enabled }} + enable-standalone-dns-proxy: {{ .Values.standaloneDnsProxy.enabled | quote }} + standalone-dns-proxy-server-port: {{ .Values.standaloneDnsProxy.serverPort | quote }} + {{- end }} +{{- end }} + {{- if ne $cniChainingMode "none" }} # Enable chaining with another CNI plugin # @@ -687,6 +753,8 @@ data: {{- if .Values.encryption.wireguard.persistentKeepalive }} wireguard-persistent-keepalive: {{ .Values.encryption.wireguard.persistentKeepalive | quote }} {{- end }} + {{- else if eq .Values.encryption.type "ztunnel" }} + enable-ztunnel: {{ .Values.encryption.enabled | quote }} {{- end }} {{- if .Values.encryption.nodeEncryption }} encrypt-node: {{ .Values.encryption.nodeEncryption | quote }} @@ -694,11 +762,20 @@ data: {{- end }} {{- if .Values.encryption.strictMode.enabled }} - enable-encryption-strict-mode: {{ .Values.encryption.strictMode.enabled | quote }} + # --- DEPRECATED: Please use encryption.strictMode.egress.enabled instead + enable-encryption-strict-mode-egress: {{ .Values.encryption.strictMode.enabled | quote }} + encryption-strict-egress-cidr: {{ .Values.encryption.strictMode.cidr | quote }} + encryption-strict-egress-allow-remote-node-identities: {{ .Values.encryption.strictMode.allowRemoteNodeIdentities | quote }} +{{- end }} - encryption-strict-mode-cidr: {{ .Values.encryption.strictMode.cidr | quote }} +{{- if .Values.encryption.strictMode.ingress.enabled }} + enable-encryption-strict-mode-ingress: {{ .Values.encryption.strictMode.ingress.enabled | quote }} +{{- end }} - encryption-strict-mode-allow-remote-node-identities: {{ .Values.encryption.strictMode.allowRemoteNodeIdentities | quote }} +{{- if .Values.encryption.strictMode.egress.enabled }} + enable-encryption-strict-mode-egress: {{ .Values.encryption.strictMode.egress.enabled | quote }} + encryption-strict-egress-cidr: {{ .Values.encryption.strictMode.egress.cidr | quote }} + encryption-strict-egress-allow-remote-node-identities: {{ .Values.encryption.strictMode.egress.allowRemoteNodeIdentities | quote }} {{- end }} enable-xt-socket-fallback: {{ .Values.enableXTSocketFallback | quote }} @@ -773,6 +850,10 @@ data: kube-proxy-replacement-healthz-bind-address: {{ default "" .Values.kubeProxyReplacementHealthzBindAddr | quote}} {{- end }} +{{- if hasKey .Values "enableNoServiceEndpointsRoutable" }} + enable-no-service-endpoints-routable: {{ .Values.enableNoServiceEndpointsRoutable | quote }} +{{- end }} + {{- if $socketLB }} {{- if hasKey $socketLB "enabled" }} bpf-lb-sock: {{ $socketLB.enabled | quote }} @@ -789,9 +870,6 @@ data: {{- end }} {{- if hasKey .Values "nodePort" }} -{{- if eq $kubeProxyReplacement "false" }} - enable-node-port: {{ .Values.nodePort.enabled | quote }} -{{- end }} {{- if hasKey .Values.nodePort "range" }} node-port-range: {{ get .Values.nodePort "range" | quote }} {{- end }} @@ -830,24 +908,18 @@ data: {{- end }} {{- if hasKey .Values.loadBalancer "serviceTopology" }} enable-service-topology: {{ .Values.loadBalancer.serviceTopology | quote }} -# {{- end }} - -{{- if hasKey .Values.loadBalancer "protocolDifferentiation" }} - bpf-lb-proto-diff: {{ .Values.loadBalancer.protocolDifferentiation.enabled | quote }} +{{- end }} {{- end }} -{{- end }} {{- if hasKey .Values.maglev "tableSize" }} bpf-lb-maglev-table-size: {{ .Values.maglev.tableSize | quote}} {{- end }} {{- if hasKey .Values.maglev "hashSeed" }} bpf-lb-maglev-hash-seed: {{ .Values.maglev.hashSeed | quote}} {{- end }} -{{- if .Values.sessionAffinity }} - enable-session-affinity: {{ .Values.sessionAffinity | quote }} -{{- end }} -{{- if .Values.svcSourceRangeCheck }} - enable-svc-source-range-check: {{ .Values.svcSourceRangeCheck | quote }} + +{{- if .Values.bpf.monitorTraceIPOption }} + ip-tracing-option-type: {{ .Values.bpf.monitorTraceIPOption | quote }} {{- end }} {{- if hasKey .Values "l2NeighDiscovery" }} @@ -860,12 +932,16 @@ data: pprof: {{ .Values.pprof.enabled | quote }} pprof-address: {{ .Values.pprof.address | quote }} pprof-port: {{ .Values.pprof.port | quote }} + pprof-mutex-profile-fraction: {{ .Values.pprof.mutexProfileFraction | quote }} + pprof-block-profile-rate: {{ .Values.pprof.blockProfileRate | quote }} {{- end }} {{- if .Values.operator.pprof.enabled }} operator-pprof: {{ .Values.operator.pprof.enabled | quote }} operator-pprof-address: {{ .Values.operator.pprof.address | quote }} operator-pprof-port: {{ .Values.operator.pprof.port | quote }} + operator-pprof-mutex-profile-fraction: {{ .Values.operator.pprof.mutexProfileFraction | quote }} + operator-pprof-block-profile-rate: {{ .Values.operator.pprof.blockProfileRate | quote }} {{- end }} {{- if .Values.logSystemLoad }} @@ -973,6 +1049,10 @@ data: # Capacity of the buffer to store recent events. hubble-event-buffer-capacity: {{ .Values.hubble.eventBufferCapacity | quote }} {{- end }} +{{- if hasKey .Values.hubble "lostEventSendInterval" }} + # Interval to send lost events from Observer server. + hubble-lost-event-send-interval: {{ include "validateDuration" .Values.hubble.lostEventSendInterval | quote }} +{{- end }} {{- if or .Values.hubble.metrics.enabled .Values.hubble.metrics.dynamic.enabled}} # Address to expose Hubble metrics (e.g. ":7070"). Metrics server will be disabled if this # field is not set. @@ -1040,8 +1120,10 @@ data: hubble-export-file-max-size-mb: {{ .Values.hubble.export.fileMaxSizeMb | default .Values.hubble.export.static.fileMaxSizeMb | quote }} hubble-export-file-max-backups: {{ .Values.hubble.export.fileMaxBackups | default .Values.hubble.export.static.fileMaxBackups | quote }} hubble-export-file-compress: {{ .Values.hubble.export.fileCompress | default .Values.hubble.export.static.fileCompress | quote }} + hubble-export-aggregation-interval: {{ include "validateDuration" .Values.hubble.export.aggregationInterval | default .Values.hubble.export.static.aggregationInterval | quote }} hubble-export-file-path: {{ .Values.hubble.export.static.filePath | quote }} hubble-export-fieldmask: {{ .Values.hubble.export.static.fieldMask | join " " | quote }} + hubble-export-fieldaggregate: {{ .Values.hubble.export.static.fieldAggregate | join " " | quote }} hubble-export-allowlist: {{ .Values.hubble.export.static.allowList | join " " | quote }} hubble-export-denylist: {{ .Values.hubble.export.static.denyList | join " " | quote }} {{- end }} @@ -1078,10 +1160,28 @@ data: disable-iptables-feeder-rules: {{ .Values.disableIptablesFeederRules | join " " | quote }} {{- end }} {{- if .Values.aksbyocni.enabled }} + {{- if or (not .Values.ipam.mode) (eq .Values.ipam.mode "cluster-pool") }} ipam: "cluster-pool" + {{- else if eq .Values.ipam.mode "multi-pool" }} + ipam: "multi-pool" + {{- end }} {{- else }} ipam: {{ $ipam | quote }} {{- end }} +{{- if .Values.ipam.nodeSpec }} + {{- if ne .Values.ipam.nodeSpec.ipamMinAllocate nil }} + ipam-min-allocate: {{ .Values.ipam.nodeSpec.ipamMinAllocate | quote }} + {{- end }} + {{- if ne .Values.ipam.nodeSpec.ipamPreAllocate nil }} + ipam-pre-allocate: {{ .Values.ipam.nodeSpec.ipamPreAllocate | quote }} + {{- end }} + {{- if ne .Values.ipam.nodeSpec.ipamMaxAllocate nil }} + ipam-max-allocate: {{ .Values.ipam.nodeSpec.ipamMaxAllocate | quote }} + {{- end }} + {{- if .Values.ipam.nodeSpec.ipamStaticIPTags }} + ipam-static-ip-tags: {{ .Values.ipam.nodeSpec.ipamStaticIPTags | join "," | quote }} + {{- end }} +{{- end }} {{- if .Values.ipam.multiPoolPreAllocation }} ipam-multi-pool-pre-allocation: {{ .Values.ipam.multiPoolPreAllocation | quote }} {{- end }} @@ -1092,18 +1192,10 @@ data: {{- if (eq $ipam "cluster-pool") }} {{- if .Values.ipv4.enabled }} - {{- if hasKey .Values.ipam.operator "clusterPoolIPv4PodCIDR" }} - {{- /* ipam.operator.clusterPoolIPv4PodCIDR removed in v1.14, remove this failsafe around v1.17 */ -}} - {{- fail "Value ipam.operator.clusterPoolIPv4PodCIDR removed, use ipam.operator.clusterPoolIPv4PodCIDRList instead" }} - {{- end }} cluster-pool-ipv4-cidr: {{ .Values.ipam.operator.clusterPoolIPv4PodCIDRList | join " " | quote }} cluster-pool-ipv4-mask-size: {{ .Values.ipam.operator.clusterPoolIPv4MaskSize | quote }} {{- end }} {{- if .Values.ipv6.enabled }} - {{- if hasKey .Values.ipam.operator "clusterPoolIPv6PodCIDR" }} - {{- /* ipam.operator.clusterPoolIPv6PodCIDR removed in v1.14, remove this failsafe around v1.17 */ -}} - {{- fail "Value ipam.operator.clusterPoolIPv6PodCIDR removed, use ipam.operator.clusterPoolIPv6PodCIDRList instead" }} - {{- end }} cluster-pool-ipv6-cidr: {{ .Values.ipam.operator.clusterPoolIPv6PodCIDRList | join " " | quote }} cluster-pool-ipv6-mask-size: {{ .Values.ipam.operator.clusterPoolIPv6MaskSize | quote }} {{- end }} @@ -1172,20 +1264,11 @@ data: crd-wait-timeout: {{ include "validateDuration" .Values.crdWaitTimeout | quote }} {{- end }} -{{- if .Values.enableK8sEndpointSlice }} - enable-k8s-endpoint-slice: {{ .Values.enableK8sEndpointSlice | quote }} -{{- end }} - {{- if hasKey .Values.k8s "serviceProxyName" }} # Configure service proxy name for Cilium. k8s-service-proxy-name: {{ .Values.k8s.serviceProxyName | quote }} {{- end }} -{{- if and .Values.customCalls .Values.customCalls.enabled }} - # Enable tail call hooks for custom eBPF programs. - enable-custom-calls: {{ .Values.customCalls.enabled | quote }} -{{- end }} - {{- if .Values.l2announcements.enabled }} # Enable L2 announcements enable-l2-announcements: {{ .Values.l2announcements.enabled | quote }} @@ -1222,6 +1305,8 @@ data: enable-pmtu-discovery: "true" {{- end }} + packetization-layer-pmtud-mode: {{ .Values.pmtuDiscovery.packetizationLayerPMTUDMode | quote }} + {{- if not .Values.securityContext.privileged }} procfs: "/host/proc" {{- end }} @@ -1296,11 +1381,16 @@ data: {{- end }} {{- if .Values.operator.unmanagedPodWatcher.restart }} - unmanaged-pod-watcher-interval: {{ .Values.operator.unmanagedPodWatcher.intervalSeconds | quote }} + {{- $interval := .Values.operator.unmanagedPodWatcher.intervalSeconds }} + unmanaged-pod-watcher-interval: {{ printf "%ds" (int $interval) | quote }} {{- else }} unmanaged-pod-watcher-interval: "0" {{- end }} +{{- if ne .Values.operator.unmanagedPodWatcher.selector nil }} + pod-restart-selector: {{ .Values.operator.unmanagedPodWatcher.selector }} +{{- end }} + {{- if .Values.dnsProxy }} {{- if hasKey .Values.dnsProxy "enableTransparentMode" }} # explicit setting gets precedence @@ -1370,10 +1460,14 @@ data: proxy-xff-num-trusted-hops-egress: {{ .Values.envoy.xffNumTrustedHopsL7PolicyEgress | quote }} proxy-connect-timeout: {{ .Values.envoy.connectTimeoutSeconds | quote }} proxy-initial-fetch-timeout: {{ .Values.envoy.initialFetchTimeoutSeconds | quote }} + proxy-max-active-downstream-connections: {{ .Values.envoy.maxGlobalDownstreamConnections | quote }} proxy-max-requests-per-connection: {{ .Values.envoy.maxRequestsPerConnection | quote }} proxy-max-connection-duration-seconds: {{ .Values.envoy.maxConnectionDurationSeconds | quote }} proxy-idle-timeout-seconds: {{ .Values.envoy.idleTimeoutDurationSeconds | quote }} proxy-max-concurrent-retries: {{ .Values.envoy.maxConcurrentRetries | quote }} + proxy-use-original-source-address: {{ .Values.envoy.useOriginalSourceAddress | quote }} + proxy-cluster-max-connections: {{ .Values.envoy.clusterMaxConnections | quote }} + proxy-cluster-max-requests: {{ .Values.envoy.clusterMaxRequests | quote }} http-retry-count: {{ .Values.envoy.httpRetryCount | quote }} http-stream-idle-timeout: {{ .Values.envoy.streamIdleTimeoutDurationSeconds | quote }} @@ -1402,13 +1496,14 @@ data: {{- if hasKey .Values.clustermesh "maxConnectedClusters" }} max-connected-clusters: {{ .Values.clustermesh.maxConnectedClusters | quote }} {{- end }} + clustermesh-cache-ttl: {{ .Values.clustermesh.cacheTTL | quote }} clustermesh-enable-endpoint-sync: {{ .Values.clustermesh.enableEndpointSliceSynchronization | quote }} - clustermesh-enable-mcs-api: {{ .Values.clustermesh.enableMCSAPISupport | quote }} + clustermesh-enable-mcs-api: {{ (or .Values.clustermesh.mcsapi.enabled .Values.clustermesh.enableMCSAPISupport) | quote }} + clustermesh-mcs-api-install-crds: {{ .Values.clustermesh.mcsapi.installCRDs | quote }} policy-default-local-cluster: {{ .Values.clustermesh.policyDefaultLocalCluster | quote }} nat-map-stats-entries: {{ .Values.nat.mapStatsEntries | quote }} nat-map-stats-interval: {{ .Values.nat.mapStatsInterval | quote }} - enable-internal-traffic-policy: {{ .Values.enableInternalTrafficPolicy | quote }} enable-lb-ipam: {{ .Values.enableLBIPAM | quote }} enable-non-default-deny-policies: {{ .Values.enableNonDefaultDenyPolicies | quote }} @@ -1420,6 +1515,14 @@ data: connectivity-probe-frequency-ratio: {{ .Values.connectivityProbeFrequencyRatio | quote }} {{- end }} +{{- if hasKey .Values "configDriftDetection" }} + enable-dynamic-config: {{ .Values.configDriftDetection.enabled | quote }} + enable-drift-checker: {{ .Values.configDriftDetection.driftChecker | quote }} + {{- if .Values.configDriftDetection.ignoredKeys }} + ignore-flags-drift-checker: {{ join "," .Values.configDriftDetection.ignoredKeys | quote }} + {{- end }} +{{- end }} + # Extra config allows adding arbitrary properties to the cilium config. # By putting it at the end of the ConfigMap, it's also possible to override existing properties. {{- if .Values.extraConfig }} diff --git a/packages/system/cilium/charts/cilium/templates/cilium-envoy/daemonset.yaml b/packages/system/cilium/charts/cilium/templates/cilium-envoy/daemonset.yaml index a5decd02..2afc2e97 100644 --- a/packages/system/cilium/charts/cilium/templates/cilium-envoy/daemonset.yaml +++ b/packages/system/cilium/charts/cilium/templates/cilium-envoy/daemonset.yaml @@ -22,10 +22,7 @@ spec: selector: matchLabels: k8s-app: cilium-envoy - {{- with .Values.envoy.updateStrategy }} - updateStrategy: - {{- toYaml . | trim | nindent 4 }} - {{- end }} + {{- include "envoy.updateStrategy" . | nindent 2 }} template: metadata: annotations: @@ -69,6 +66,7 @@ spec: securityContext: {{- toYaml . | nindent 8 }} {{- end }} + {{- include "envoy.initContainers" . | nindent 6 }} containers: - name: cilium-envoy image: {{ include "cilium.image" .Values.envoy.image | quote }} @@ -94,6 +92,7 @@ spec: {{- if .Values.envoy.log.path }} - '--log-path {{ .Values.envoy.log.path }}' {{- end }} + {{- include "envoy.args.extra" . | nindent 8 }} {{- with .Values.envoy.extraArgs }} {{- toYaml . | trim | nindent 8 }} {{- end }} @@ -157,6 +156,7 @@ spec: - name: KUBERNETES_SERVICE_PORT value: {{ include "k8sServicePort" . }} {{- end }} + {{- include "envoy.env.extra" . | nindent 8 }} {{- with .Values.envoy.extraEnv }} {{- toYaml . | trim | nindent 8 }} {{- end }} @@ -164,19 +164,7 @@ spec: resources: {{- toYaml . | trim | nindent 10 }} {{- end }} - {{- if .Values.envoy.prometheus.enabled }} - ports: - - name: envoy-metrics - containerPort: {{ .Values.envoy.prometheus.port }} - hostPort: {{ .Values.envoy.prometheus.port }} - protocol: TCP - {{- if and .Values.envoy.debug.admin.enabled .Values.envoy.debug.admin.port }} - - name: envoy-admin - containerPort: {{ .Values.envoy.debug.admin.port }} - hostPort: {{ .Values.envoy.debug.admin.port }} - protocol: TCP - {{- end }} - {{- end }} + {{- include "envoy.ports" . }} securityContext: {{- if .Values.envoy.securityContext.privileged }} privileged: true @@ -209,6 +197,7 @@ spec: mountPath: /sys/fs/bpf mountPropagation: HostToContainer {{- end }} + {{- include "envoy.volumeMounts.extra" . | nindent 8 }} {{- range .Values.envoy.extraHostPathMounts }} - name: {{ .name }} mountPath: {{ .mountPath }} @@ -232,10 +221,7 @@ spec: {{- if .Values.envoy.dnsPolicy }} dnsPolicy: {{ .Values.envoy.dnsPolicy }} {{- end }} - {{- with .Values.envoy.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} + {{- include "envoy.affinity" . | nindent 6 }} {{- with .Values.envoy.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} @@ -269,6 +255,7 @@ spec: path: /sys/fs/bpf type: DirectoryOrCreate {{- end }} + {{- include "envoy.hostPathMounts.extra" . | nindent 4 }} {{- range .Values.envoy.extraHostPathMounts }} - name: {{ .name }} hostPath: diff --git a/packages/system/cilium/charts/cilium/templates/cilium-envoy/service.yaml b/packages/system/cilium/charts/cilium/templates/cilium-envoy/service.yaml index 6b982c28..8da47943 100644 --- a/packages/system/cilium/charts/cilium/templates/cilium-envoy/service.yaml +++ b/packages/system/cilium/charts/cilium/templates/cilium-envoy/service.yaml @@ -32,5 +32,5 @@ spec: - name: envoy-metrics port: {{ .Values.envoy.prometheus.port }} protocol: TCP - targetPort: envoy-metrics + targetPort: {{ .Values.envoy.prometheus.port }} {{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/cilium-ingress-service.yaml b/packages/system/cilium/charts/cilium/templates/cilium-ingress-service.yaml index 5d16d7ca..f25afeee 100644 --- a/packages/system/cilium/charts/cilium/templates/cilium-ingress-service.yaml +++ b/packages/system/cilium/charts/cilium/templates/cilium-ingress-service.yaml @@ -49,8 +49,8 @@ spec: externalTrafficPolicy: {{ .Values.ingressController.service.externalTrafficPolicy }} {{- end }} --- -apiVersion: v1 -kind: Endpoints +apiVersion: discovery.k8s.io/v1 +kind: EndpointSlice metadata: name: {{ .Values.ingressController.service.name }} namespace: {{ include "cilium.namespace" . }} @@ -65,9 +65,10 @@ metadata: annotations: {{- toYaml .Values.ingressController.service.annotations | nindent 4 }} {{- end }} -subsets: +addressType: IPv4 +endpoints: - addresses: - - ip: "192.192.192.192" - ports: - - port: 9999 + - "192.192.192.192" +ports: +- port: 9999 {{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/cilium-nodeinit/daemonset.yaml b/packages/system/cilium/charts/cilium/templates/cilium-nodeinit/daemonset.yaml index add6ae5a..4edbeada 100644 --- a/packages/system/cilium/charts/cilium/templates/cilium-nodeinit/daemonset.yaml +++ b/packages/system/cilium/charts/cilium/templates/cilium-nodeinit/daemonset.yaml @@ -122,6 +122,8 @@ spec: {{- if .Values.serviceAccounts.nodeinit.enabled }} serviceAccountName: {{ .Values.serviceAccounts.nodeinit.name | quote }} automountServiceAccountToken: {{ .Values.serviceAccounts.nodeinit.automount }} + {{- else }} + automountServiceAccountToken: false {{- end }} {{- with .Values.nodeinit.extraVolumes }} volumes: diff --git a/packages/system/cilium/charts/cilium/templates/cilium-operator/clusterrole.yaml b/packages/system/cilium/charts/cilium/templates/cilium-operator/clusterrole.yaml index 4fcb5985..c147e2b5 100644 --- a/packages/system/cilium/charts/cilium/templates/cilium-operator/clusterrole.yaml +++ b/packages/system/cilium/charts/cilium/templates/cilium-operator/clusterrole.yaml @@ -69,7 +69,7 @@ rules: resources: - endpointslices verbs: -{{- if or .Values.clustermesh.enableEndpointSliceSynchronization .Values.clustermesh.enableMCSAPISupport }} +{{- if or .Values.clustermesh.enableEndpointSliceSynchronization .Values.clustermesh.mcsapi.enabled .Values.clustermesh.enableMCSAPISupport }} - create - update - delete @@ -78,6 +78,17 @@ rules: - get - list - watch +{{- if .Values.clustermesh.enableEndpointSliceSynchronization }} +- apiGroups: + - "" + resources: + # The controller needs to be able to set a service's finalizers to be able to create an EndpointSlice + # resource that is owned by the service and sets blockOwnerDeletion=true in its ownerRef. + # This is required when the admission plugin OwnerReferencesPermissionEnforcement is activated. + - services/finalizers + verbs: + - update +{{- end }} - apiGroups: - "" resources: @@ -114,6 +125,20 @@ rules: - delete - patch {{- end }} +{{- if or .Values.ingressController.enabled .Values.gatewayAPI.enabled }} +- apiGroups: + - "discovery.k8s.io" + resources: + - endpointslices + verbs: + - get + - list + - watch + - create + - update + - delete + - patch +{{- end }} {{- if .Values.clustermesh.enableEndpointSliceSynchronization }} - apiGroups: - "" @@ -227,7 +252,6 @@ rules: - update resourceNames: - ciliumloadbalancerippools.cilium.io - - ciliumbgppeeringpolicies.cilium.io - ciliumbgpclusterconfigs.cilium.io - ciliumbgppeerconfigs.cilium.io - ciliumbgpadvertisements.cilium.io @@ -248,12 +272,15 @@ rules: - ciliuml2announcementpolicies.cilium.io - ciliumpodippools.cilium.io - ciliumgatewayclassconfigs.cilium.io +{{- if and (or .Values.clustermesh.mcsapi.enabled .Values.clustermesh.enableMCSAPISupport) .Values.clustermesh.mcsapi.installCRDs }} + - serviceimports.multicluster.x-k8s.io + - serviceexports.multicluster.x-k8s.io +{{- end }} - apiGroups: - cilium.io resources: - ciliumloadbalancerippools - ciliumpodippools - - ciliumbgppeeringpolicies - ciliumbgpclusterconfigs - ciliumbgpnodeconfigoverrides - ciliumbgppeerconfigs @@ -301,6 +328,10 @@ rules: - networking.k8s.io resources: - ingresses/status # To update ingress status with load balancer IP. + # The controller needs to be able to set ingress finalizers to be able to create a CiliumEnvoyConfig + # resource that is owned by the ingress, and set blockOwnerDeletion=true in its ownerRef. + # This is required when the admission plugin OwnerReferencesPermissionEnforcement is activated. + - ingresses/finalizers verbs: - update {{- end }} @@ -352,7 +383,7 @@ rules: - update - patch {{- end }} -{{- if or .Values.gatewayAPI.enabled .Values.clustermesh.enableMCSAPISupport }} +{{- if or .Values.gatewayAPI.enabled .Values.clustermesh.mcsapi.enabled .Values.clustermesh.enableMCSAPISupport }} - apiGroups: - multicluster.x-k8s.io resources: @@ -361,14 +392,14 @@ rules: - get - list - watch -{{- if .Values.clustermesh.enableMCSAPISupport }} +{{- if or .Values.clustermesh.mcsapi.enabled .Values.clustermesh.enableMCSAPISupport }} - create - update - patch - delete {{- end }} {{- end }} -{{- if .Values.clustermesh.enableMCSAPISupport }} +{{- if or .Values.clustermesh.mcsapi.enabled .Values.clustermesh.enableMCSAPISupport }} - apiGroups: - multicluster.x-k8s.io resources: @@ -376,6 +407,15 @@ rules: verbs: - update - patch +- apiGroups: + - multicluster.x-k8s.io + resources: + # The controller needs to be able to set serviceimport finalizers to be able to create a derived Service + # resource that is owned by the ServiceImport and sets blockOwnerDeletion=true in its ownerRef. + # This is required when the admission plugin OwnerReferencesPermissionEnforcement is activated. + - serviceimports/finalizers + verbs: + - update - apiGroups: - multicluster.x-k8s.io resources: @@ -401,4 +441,10 @@ rules: - patch - delete {{- end }} +- apiGroups: + - cilium.io + resources: + - ciliumendpointslices + verbs: + - deletecollection {{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/cilium-operator/deployment.yaml b/packages/system/cilium/charts/cilium/templates/cilium-operator/deployment.yaml index 606807f3..f4cddff8 100644 --- a/packages/system/cilium/charts/cilium/templates/cilium-operator/deployment.yaml +++ b/packages/system/cilium/charts/cilium/templates/cilium-operator/deployment.yaml @@ -98,7 +98,7 @@ spec: fieldRef: apiVersion: v1 fieldPath: metadata.namespace - {{- if or .Values.clustermesh.enableEndpointSliceSynchronization .Values.clustermesh.enableMCSAPISupport }} + {{- if or .Values.clustermesh.enableEndpointSliceSynchronization .Values.clustermesh.mcsapi.enabled .Values.clustermesh.enableMCSAPISupport }} - name: CILIUM_CLUSTERMESH_CONFIG value: /var/lib/cilium/clustermesh/ {{- end }} @@ -170,6 +170,7 @@ spec: - name: AZURE_RESOURCE_GROUP value: {{ .Values.azure.resourceGroup }} {{- end }} + {{- if .Values.azure.clientID }} - name: AZURE_CLIENT_ID valueFrom: secretKeyRef: @@ -181,11 +182,15 @@ spec: name: cilium-azure key: AZURE_CLIENT_SECRET {{- end }} + {{- end }} {{- with .Values.operator.extraEnv }} {{- toYaml . | nindent 8 }} {{- end }} - {{- if .Values.operator.prometheus.enabled }} ports: + - name: health + containerPort: 9234 + hostPort: 9234 + {{- if .Values.operator.prometheus.enabled }} - name: prometheus containerPort: {{ .Values.operator.prometheus.port }} {{- if .Values.operator.hostNetwork }} @@ -199,7 +204,7 @@ spec: host: {{ .Values.ipv4.enabled | ternary "127.0.0.1" "::1" | quote }} {{- end }} path: /healthz - port: 9234 + port: health scheme: HTTP initialDelaySeconds: 60 periodSeconds: 10 @@ -210,7 +215,7 @@ spec: host: {{ .Values.ipv4.enabled | ternary "127.0.0.1" "::1" | quote }} {{- end }} path: /healthz - port: 9234 + port: health scheme: HTTP initialDelaySeconds: 0 periodSeconds: 5 @@ -230,7 +235,7 @@ spec: readOnly: true {{- end }} {{- end }} - {{- if or .Values.clustermesh.enableEndpointSliceSynchronization .Values.clustermesh.enableMCSAPISupport }} + {{- if or .Values.clustermesh.enableEndpointSliceSynchronization .Values.clustermesh.mcsapi.enabled .Values.clustermesh.enableMCSAPISupport }} - name: clustermesh-secrets mountPath: /var/lib/cilium/clustermesh readOnly: true @@ -245,6 +250,11 @@ spec: mountPath: {{ dir .Values.authentication.mutual.spire.agentSocketPath }} readOnly: true {{- end }} + {{- if and .Values.operator.prometheus.enabled .Values.operator.prometheus.tls.enabled }} + - name: prometheus-tls + mountPath: /var/lib/cilium/tls/prometheus + readOnly: true + {{- end }} {{- range .Values.operator.extraHostPathMounts }} - name: {{ .name }} mountPath: {{ .mountPath }} @@ -256,13 +266,15 @@ spec: {{- with .Values.operator.extraVolumeMounts }} {{- toYaml . | nindent 8 }} {{- end }} + {{- include "cilium-operator.volumeMounts.extra" . | nindent 8 }} {{- with .Values.operator.resources }} resources: {{- toYaml . | trim | nindent 10 }} {{- end }} - {{- with .Values.operator.securityContext }} + {{- $sc := include "cilium.operator.securityContext" . | trim }} + {{- if $sc }} securityContext: - {{- toYaml . | trim | nindent 10 }} + {{- $sc | nindent 10 }} {{- end }} terminationMessagePolicy: FallbackToLogsOnError hostNetwork: {{ .Values.operator.hostNetwork }} @@ -295,23 +307,25 @@ spec: nodeSelector: {{- toYaml . | trim | nindent 8 }} {{- end }} - {{- if and (or .Values.clustermesh.enableEndpointSliceSynchronization .Values.clustermesh.enableMCSAPISupport) .Values.clustermesh.config.enabled (not (and .Values.clustermesh.useAPIServer .Values.clustermesh.apiserver.kvstoremesh.enabled )) }} + {{- if and (or .Values.clustermesh.enableEndpointSliceSynchronization .Values.clustermesh.mcsapi.enabled .Values.clustermesh.enableMCSAPISupport) .Values.clustermesh.config.enabled (not (and .Values.clustermesh.useAPIServer .Values.clustermesh.apiserver.kvstoremesh.enabled )) }} hostAliases: - {{- range $cluster := .Values.clustermesh.config.clusters }} + {{- range $_, $cluster := (include "clustermesh-clusters" . | fromJson) }} {{- range $ip := $cluster.ips }} - ip: {{ $ip }} hostnames: [ "{{ $cluster.name }}.{{ $.Values.clustermesh.config.domain }}" ] {{- end }} {{- end }} {{- end }} - {{- with .Values.operator.tolerations }} + {{- if or (.Values.operator.tolerations) (hasKey .Values "agentNotReadyTaintKey") }} tolerations: + {{- with .Values.operator.tolerations }} {{- toYaml . | trim | nindent 8 }} {{- end }} {{- if hasKey .Values "agentNotReadyTaintKey" }} - key: {{ .Values.agentNotReadyTaintKey }} operator: Exists {{ end}} + {{- end}} volumes: # To read the configuration from the config map - name: cilium-config-path @@ -360,7 +374,7 @@ spec: {{- with .Values.operator.extraVolumes }} {{- toYaml . | nindent 6 }} {{- end }} - {{- if or .Values.clustermesh.enableEndpointSliceSynchronization .Values.clustermesh.enableMCSAPISupport }} + {{- if or .Values.clustermesh.enableEndpointSliceSynchronization .Values.clustermesh.mcsapi.enabled .Values.clustermesh.enableMCSAPISupport }} # To read the clustermesh configuration - name: clustermesh-secrets projected: @@ -417,4 +431,25 @@ spec: path: local-etcd-client-ca.crt {{- end }} {{- end }} + {{- if and .Values.operator.prometheus.enabled .Values.operator.prometheus.tls.enabled }} + # To read the prometheus configuration + - name: prometheus-tls + projected: + # note: the leading zero means this number is in octal representation: do not remove it + defaultMode: 0400 + sources: + - secret: + name: {{ .Values.operator.prometheus.tls.server.existingSecret }} + optional: true + items: + - key: tls.crt + path: server.crt + - key: tls.key + path: server.key + {{- if .Values.operator.prometheus.tls.server.mtls.enabled }} + - key: ca.crt + path: client-ca.crt + {{- end }} + {{- end }} + {{- include "cilium-operator.volumes.extra" . | nindent 6 }} {{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/cilium-operator/role.yaml b/packages/system/cilium/charts/cilium/templates/cilium-operator/role.yaml index 8f7acd9f..11515ced 100644 --- a/packages/system/cilium/charts/cilium/templates/cilium-operator/role.yaml +++ b/packages/system/cilium/charts/cilium/templates/cilium-operator/role.yaml @@ -83,3 +83,35 @@ rules: - update - patch {{- end }} + +{{- if and .Values.operator.enabled .Values.serviceAccounts.operator.create }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: cilium-operator-ztunnel + namespace: {{ include "cilium.namespace" . }} + {{- with .Values.operator.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + app.kubernetes.io/part-of: cilium + {{- with .Values.commonLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} +rules: +# ZTunnel DaemonSet management permissions +# Note: These permissions must always be granted (not conditional on encryption.type) +# because the controller needs to clean up stale DaemonSets when ztunnel is disabled. +- apiGroups: + - apps + resources: + - daemonsets + verbs: + - create + - delete + - get + - list + - watch +{{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/cilium-operator/rolebinding.yaml b/packages/system/cilium/charts/cilium/templates/cilium-operator/rolebinding.yaml index 56b89209..22ac17bc 100644 --- a/packages/system/cilium/charts/cilium/templates/cilium-operator/rolebinding.yaml +++ b/packages/system/cilium/charts/cilium/templates/cilium-operator/rolebinding.yaml @@ -77,3 +77,29 @@ subjects: name: {{ .Values.serviceAccounts.operator.name | quote }} namespace: {{ include "cilium.namespace" . }} {{- end }} + +{{- if and .Values.operator.enabled .Values.serviceAccounts.operator.create }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: cilium-operator-ztunnel + namespace: {{ include "cilium.namespace" . }} + labels: + app.kubernetes.io/part-of: cilium + {{- with .Values.commonLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.operator.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: cilium-operator-ztunnel +subjects: +- kind: ServiceAccount + name: {{ .Values.serviceAccounts.operator.name | quote }} + namespace: {{ include "cilium.namespace" . }} +{{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/cilium-operator/secret.yaml b/packages/system/cilium/charts/cilium/templates/cilium-operator/secret.yaml index 4ac55d7a..6346e136 100644 --- a/packages/system/cilium/charts/cilium/templates/cilium-operator/secret.yaml +++ b/packages/system/cilium/charts/cilium/templates/cilium-operator/secret.yaml @@ -1,5 +1,6 @@ {{- if .Values.operator.enabled }} {{- if .Values.azure.enabled }} +{{- if .Values.azure.clientID }} apiVersion: v1 kind: Secret metadata: @@ -19,3 +20,4 @@ data: AZURE_CLIENT_SECRET: {{ default "" .Values.azure.clientSecret | b64enc | quote }} {{- end }} {{- end }} +{{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/cilium-preflight/clusterrole.yaml b/packages/system/cilium/charts/cilium/templates/cilium-preflight/clusterrole.yaml index bf0f07da..fbf511cd 100644 --- a/packages/system/cilium/charts/cilium/templates/cilium-preflight/clusterrole.yaml +++ b/packages/system/cilium/charts/cilium/templates/cilium-preflight/clusterrole.yaml @@ -94,7 +94,6 @@ rules: - cilium.io resources: - ciliumloadbalancerippools - - ciliumbgppeeringpolicies - ciliumbgpnodeconfigs - ciliumbgpadvertisements - ciliumbgppeerconfigs diff --git a/packages/system/cilium/charts/cilium/templates/cilium-secrets-namespace.yaml b/packages/system/cilium/charts/cilium/templates/cilium-secrets-namespace.yaml index 944b0629..600ccfeb 100644 --- a/packages/system/cilium/charts/cilium/templates/cilium-secrets-namespace.yaml +++ b/packages/system/cilium/charts/cilium/templates/cilium-secrets-namespace.yaml @@ -20,6 +20,9 @@ metadata: {{- with $.Values.commonLabels }} {{- toYaml . | nindent 4 }} {{- end }} + {{- with $.Values.secretsNamespaceLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} annotations: {{- with $.Values.secretsNamespaceAnnotations }} {{- toYaml . | nindent 4 }} diff --git a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/clusterrole.yaml b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/clusterrole.yaml index 8565f585..88288eb8 100644 --- a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/clusterrole.yaml +++ b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/clusterrole.yaml @@ -50,7 +50,7 @@ rules: - get - list - watch -{{- if .Values.clustermesh.enableMCSAPISupport }} +{{- if or .Values.clustermesh.mcsapi.enabled .Values.clustermesh.enableMCSAPISupport }} - apiGroups: - multicluster.x-k8s.io resources: diff --git a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/deployment.yaml b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/deployment.yaml index b0366e3b..1146209a 100644 --- a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/deployment.yaml +++ b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/deployment.yaml @@ -137,6 +137,7 @@ spec: - --advertise-client-urls=https://localhost:2379 - --initial-cluster-token=$(INITIAL_CLUSTER_TOKEN) - --auto-compaction-retention=1 + - --enable-grpc-gateway=false {{- if .Values.clustermesh.apiserver.metrics.etcd.enabled }} - --listen-metrics-urls=http://0.0.0.0:{{ .Values.clustermesh.apiserver.metrics.etcd.port }} - --metrics={{ .Values.clustermesh.apiserver.metrics.etcd.mode }} @@ -208,12 +209,13 @@ spec: - --prometheus-serve-addr=:{{ .Values.clustermesh.apiserver.metrics.port }} - --controller-group-metrics=all {{- end }} - {{- if .Values.clustermesh.enableMCSAPISupport }} + {{- if or .Values.clustermesh.mcsapi.enabled .Values.clustermesh.enableMCSAPISupport }} - --clustermesh-enable-mcs-api {{- end }} {{- if .Values.ciliumEndpointSlice.enabled }} - --enable-cilium-endpoint-slice {{- end }} + {{- include "clustermesh.apiserver.args.extra" . | nindent 8 }} {{- with .Values.clustermesh.apiserver.extraArgs }} {{- toYaml . | trim | nindent 8 }} {{- end }} @@ -303,12 +305,14 @@ spec: {{- if hasKey .Values.clustermesh "maxConnectedClusters" }} - --max-connected-clusters={{ .Values.clustermesh.maxConnectedClusters }} {{- end }} + - --clustermesh-cache-ttl={{ .Values.clustermesh.cacheTTL }} - --health-port={{ .Values.clustermesh.apiserver.kvstoremesh.healthPort }} {{- if .Values.clustermesh.apiserver.metrics.kvstoremesh.enabled }} - --prometheus-serve-addr=:{{ .Values.clustermesh.apiserver.metrics.kvstoremesh.port }} - --controller-group-metrics=all {{- end }} - --enable-heartbeat={{ eq "true" (include "identityAllocationCRD" .) | ternary "false" "true" }} + {{- include "clustermesh.kvstoremesh.args.extra" . | nindent 8 }} {{- with .Values.clustermesh.apiserver.kvstoremesh.extraArgs }} {{- toYaml . | trim | nindent 8 }} {{- end }} @@ -505,7 +509,7 @@ spec: {{- end }} {{- if and .Values.clustermesh.config.enabled .Values.clustermesh.apiserver.kvstoremesh.enabled }} hostAliases: - {{- range $cluster := .Values.clustermesh.config.clusters }} + {{- range $_, $cluster := (include "clustermesh-clusters" . | fromJson) }} {{- range $ip := $cluster.ips }} - ip: {{ $ip }} hostnames: [ "{{ $cluster.name }}.{{ $.Values.clustermesh.config.domain }}" ] diff --git a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/service.yaml b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/service.yaml index 6ef3f63f..5e20234b 100644 --- a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/service.yaml +++ b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/service.yaml @@ -1,4 +1,4 @@ -{{- if and .Values.clustermesh.useAPIServer (eq .Values.clustermesh.apiserver.kvstoremesh.kvstoreMode "internal") }} +{{- if and .Values.clustermesh.useAPIServer (eq .Values.clustermesh.apiserver.kvstoremesh.kvstoreMode "internal") (not .Values.clustermesh.apiserver.service.externallyCreated) }} apiVersion: v1 kind: Service metadata: diff --git a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-cronjob/_job-spec.tpl b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-cronjob/_job-spec.tpl index 7ba8cb12..2fdccf47 100644 --- a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-cronjob/_job-spec.tpl +++ b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-cronjob/_job-spec.tpl @@ -9,10 +9,18 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} spec: + securityContext: + seccompProfile: + type: RuntimeDefault containers: - name: certgen image: {{ include "cilium.image" .Values.certgen.image | quote }} imagePullPolicy: {{ .Values.certgen.image.pullPolicy }} + securityContext: + capabilities: + drop: + - ALL + allowPrivilegeEscalation: false {{- with .Values.certgen.resources }} resources: {{- toYaml . | nindent 12 }} @@ -84,7 +92,7 @@ spec: volumeMounts: {{- toYaml . | nindent 10 }} {{- end }} - hostNetwork: true + hostNetwork: false {{- with .Values.certgen.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} @@ -96,7 +104,6 @@ spec: tolerations: {{- toYaml . | nindent 8 }} {{- end }} - serviceAccount: {{ .Values.serviceAccounts.clustermeshcertgen.name | quote }} serviceAccountName: {{ .Values.serviceAccounts.clustermeshcertgen.name | quote }} automountServiceAccountToken: {{ .Values.serviceAccounts.clustermeshcertgen.automount }} {{- with .Values.imagePullSecrets }} @@ -108,9 +115,11 @@ spec: volumes: {{- toYaml . | nindent 6 }} {{- end }} - affinity: {{- with .Values.certgen.affinity }} + affinity: {{- toYaml . | nindent 8 }} {{- end }} - ttlSecondsAfterFinished: {{ .Values.certgen.ttlSecondsAfterFinished }} + {{- with .Values.certgen.ttlSecondsAfterFinished }} + ttlSecondsAfterFinished: {{ . }} + {{- end }} {{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-cronjob/cronjob.yaml b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-cronjob/cronjob.yaml index ebda21bd..4b88d92c 100644 --- a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-cronjob/cronjob.yaml +++ b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-cronjob/cronjob.yaml @@ -16,6 +16,8 @@ metadata: {{- end }} spec: schedule: {{ .Values.clustermesh.apiserver.tls.auto.schedule | quote }} + successfulJobsHistoryLimit: {{ .Values.certgen.cronJob.successfulJobsHistoryLimit }} + failedJobsHistoryLimit: {{ .Values.certgen.cronJob.failedJobsHistoryLimit }} concurrencyPolicy: Forbid jobTemplate: {{- include "clustermesh-apiserver-generate-certs.job.spec" . | nindent 4 }} diff --git a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-cronjob/job.yaml b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-cronjob/job.yaml index 72f8dc60..574aee13 100644 --- a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-cronjob/job.yaml +++ b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-cronjob/job.yaml @@ -1,9 +1,18 @@ {{- if and (and .Values.clustermesh.useAPIServer (eq .Values.clustermesh.apiserver.kvstoremesh.kvstoreMode "internal")) .Values.clustermesh.apiserver.tls.auto.enabled (eq .Values.clustermesh.apiserver.tls.auto.method "cronJob") }} +{{/* +Because Kubernetes job specs are immutable, Helm will fail patch this job if +the spec changes between releases. To avoid breaking the upgrade path, we +generate a name for the job here which is based on the checksum of the spec. +This will cause the name of the job to change if its content changes, +and in turn cause Helm to do delete the old job and replace it with a new one. +*/}} +{{- $jobSpec := include "clustermesh-apiserver-generate-certs.job.spec" . -}} +{{- $checkSum := $jobSpec | sha256sum | trunc 10 -}} --- apiVersion: batch/v1 kind: Job metadata: - name: clustermesh-apiserver-generate-certs + name: clustermesh-apiserver-generate-certs-{{$checkSum}} namespace: {{ include "cilium.namespace" . }} labels: k8s-app: clustermesh-apiserver-generate-certs @@ -11,13 +20,14 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} app.kubernetes.io/part-of: cilium + {{- if or .Values.certgen.annotations.job .Values.clustermesh.annotations }} annotations: - "helm.sh/hook": post-install,post-upgrade {{- with .Values.certgen.annotations.job }} - {{- toYaml . | nindent 4 }} + {{- toYaml . | nindent 4 }} {{- end }} {{- with .Values.clustermesh.annotations }} {{- toYaml . | nindent 4 }} {{- end }} -{{ include "clustermesh-apiserver-generate-certs.job.spec" . }} + {{- end }} +{{ $jobSpec }} {{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-cronjob/role.yaml b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-cronjob/role.yaml index 6e822701..1f6dc153 100644 --- a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-cronjob/role.yaml +++ b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-cronjob/role.yaml @@ -38,7 +38,6 @@ rules: - clustermesh-apiserver-admin-cert - clustermesh-apiserver-remote-cert - clustermesh-apiserver-local-cert - - clustermesh-apiserver-client-cert verbs: - update {{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-helm/admin-secret.yaml b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-helm/admin-secret.yaml index fa49c9ce..49325d14 100644 --- a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-helm/admin-secret.yaml +++ b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-helm/admin-secret.yaml @@ -8,12 +8,16 @@ kind: Secret metadata: name: clustermesh-apiserver-admin-cert namespace: {{ include "cilium.namespace" . }} - {{- with .Values.commonLabels }} labels: + {{- with .Values.commonLabels }} {{- toYaml . | nindent 4 }} {{- end }} - {{- with .Values.clustermesh.annotations }} + cilium.io/helm-template-non-idempotent: "true" annotations: + {{- with .Values.clustermesh.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.nonIdempotentAnnotations }} {{- toYaml . | nindent 4 }} {{- end }} type: kubernetes.io/tls diff --git a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-helm/local-secret.yaml b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-helm/local-secret.yaml index 0589a9e7..3d34bd68 100644 --- a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-helm/local-secret.yaml +++ b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-helm/local-secret.yaml @@ -8,12 +8,16 @@ kind: Secret metadata: name: clustermesh-apiserver-local-cert namespace: {{ include "cilium.namespace" . }} - {{- with .Values.commonLabels }} labels: + {{- with .Values.commonLabels }} {{- toYaml . | nindent 4 }} {{- end }} - {{- with .Values.clustermesh.annotations }} + cilium.io/helm-template-non-idempotent: "true" annotations: + {{- with .Values.clustermesh.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.nonIdempotentAnnotations }} {{- toYaml . | nindent 4 }} {{- end }} type: kubernetes.io/tls diff --git a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-helm/remote-secret.yaml b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-helm/remote-secret.yaml index 42afe0fe..11bfc51d 100644 --- a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-helm/remote-secret.yaml +++ b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-helm/remote-secret.yaml @@ -8,12 +8,16 @@ kind: Secret metadata: name: clustermesh-apiserver-remote-cert namespace: {{ include "cilium.namespace" . }} - {{- with .Values.commonLabels }} labels: + {{- with .Values.commonLabels }} {{- toYaml . | nindent 4 }} {{- end }} - {{- with .Values.clustermesh.annotations }} + cilium.io/helm-template-non-idempotent: "true" annotations: + {{- with .Values.clustermesh.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.nonIdempotentAnnotations }} {{- toYaml . | nindent 4 }} {{- end }} type: kubernetes.io/tls diff --git a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-helm/server-secret.yaml b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-helm/server-secret.yaml index e49478cb..0df9a20d 100644 --- a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-helm/server-secret.yaml +++ b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/tls-helm/server-secret.yaml @@ -10,12 +10,16 @@ kind: Secret metadata: name: clustermesh-apiserver-server-cert namespace: {{ include "cilium.namespace" . }} - {{- with .Values.commonLabels }} labels: + {{- with .Values.commonLabels }} {{- toYaml . | nindent 4 }} {{- end }} - {{- with .Values.clustermesh.annotations }} + cilium.io/helm-template-non-idempotent: "true" annotations: + {{- with .Values.clustermesh.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.nonIdempotentAnnotations }} {{- toYaml . | nindent 4 }} {{- end }} type: kubernetes.io/tls diff --git a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/users-configmap.yaml b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/users-configmap.yaml index c736eb1e..d742e4e7 100644 --- a/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/users-configmap.yaml +++ b/packages/system/cilium/charts/cilium/templates/clustermesh-apiserver/users-configmap.yaml @@ -1,5 +1,5 @@ {{- if and - (and .Values.clustermesh.useAPIServer (eq .Values.clustermesh.apiserver.kvstoremesh.kvstoreMode "internal") (eq "true" (include "identityAllocationCRD" .))) + (and .Values.clustermesh.useAPIServer .Values.clustermesh.config.enabled (eq .Values.clustermesh.apiserver.kvstoremesh.kvstoreMode "internal") (eq "true" (include "identityAllocationCRD" .))) (ne .Values.clustermesh.apiserver.tls.authMode "legacy") }} --- @@ -21,7 +21,7 @@ metadata: data: users.yaml: | users: - {{- range .Values.clustermesh.config.clusters }} + {{- range (include "clustermesh-clusters" . | fromJson) }} - name: remote-{{ .name }} role: remote {{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/clustermesh-config/_helpers.tpl b/packages/system/cilium/charts/cilium/templates/clustermesh-config/_helpers.tpl index 47c3393c..abe9bdb8 100644 --- a/packages/system/cilium/charts/cilium/templates/clustermesh-config/_helpers.tpl +++ b/packages/system/cilium/charts/cilium/templates/clustermesh-config/_helpers.tpl @@ -43,3 +43,26 @@ key-file: /var/lib/cilium/clustermesh/{{ $prefix }}etcd-client.key cert-file: /var/lib/cilium/clustermesh/{{ $prefix }}etcd-client.crt {{- end }} {{- end }} + +{{- define "clustermesh-clusters" }} +{{- $clusters := dict }} +{{- if kindIs "map" .Values.clustermesh.config.clusters }} + {{- range $name, $cluster := deepCopy .Values.clustermesh.config.clusters }} + {{- if ne $cluster.enabled false }} + {{- $_ := unset $cluster "enabled" }} + {{- $_ = set $cluster "name" $name }} + {{- $_ = set $clusters $name $cluster }} + {{- end }} + {{- end }} +{{- else if kindIs "slice" .Values.clustermesh.config.clusters }} + {{- range $cluster := deepCopy .Values.clustermesh.config.clusters }} + {{- if ne $cluster.enabled false }} + {{- $_ := unset $cluster "enabled" }} + {{- $_ := set $clusters $cluster.name $cluster }} + {{- end }} + {{- end }} +{{- else }} + {{- fail (printf "unknown type %s for clustermesh.config.clusters" (kindOf .Values.clustermesh.config.clusters)) }} +{{- end }} +{{- toJson $clusters }} +{{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/clustermesh-config/clustermesh-secret.yaml b/packages/system/cilium/charts/cilium/templates/clustermesh-config/clustermesh-secret.yaml index 5a3a7aa8..d60d6f1c 100644 --- a/packages/system/cilium/charts/cilium/templates/clustermesh-config/clustermesh-secret.yaml +++ b/packages/system/cilium/charts/cilium/templates/clustermesh-config/clustermesh-secret.yaml @@ -18,7 +18,7 @@ data: {{- $kvstoremesh := and .Values.clustermesh.useAPIServer .Values.clustermesh.apiserver.kvstoremesh.enabled }} {{- $override := ternary (printf "https://clustermesh-apiserver.%s.svc:2379" (include "cilium.namespace" .)) "" $kvstoremesh }} {{- $local_etcd := and $kvstoremesh (eq .Values.clustermesh.apiserver.kvstoremesh.kvstoreMode "external") }} - {{- range .Values.clustermesh.config.clusters }} + {{- range (include "clustermesh-clusters" . | fromJson) }} {{ .name }}: {{ include "clustermesh-config-generate-etcd-cfg" (list . $.Values.clustermesh.config.domain $override $local_etcd $.Values.etcd ) | b64enc }} {{- /* The parenthesis around .tls are required, since it can be null: https://stackoverflow.com/a/68807258 */}} {{- if and (eq $override "") (.tls).cert (.tls).key }} diff --git a/packages/system/cilium/charts/cilium/templates/clustermesh-config/kvstoremesh-secret.yaml b/packages/system/cilium/charts/cilium/templates/clustermesh-config/kvstoremesh-secret.yaml index 3cdc7828..2828cfd2 100644 --- a/packages/system/cilium/charts/cilium/templates/clustermesh-config/kvstoremesh-secret.yaml +++ b/packages/system/cilium/charts/cilium/templates/clustermesh-config/kvstoremesh-secret.yaml @@ -15,7 +15,7 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} data: - {{- range .Values.clustermesh.config.clusters }} + {{- range (include "clustermesh-clusters" . | fromJson) }} {{ .name }}: {{ include "clustermesh-config-generate-etcd-cfg" (list . $.Values.clustermesh.config.domain "" false $.Values.etcd ) | b64enc }} {{- /* The parenthesis around .tls are required, since it can be null: https://stackoverflow.com/a/68807258 */}} {{- if and (.tls).cert (.tls).key }} diff --git a/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/clusterrole.yaml b/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/clusterrole.yaml new file mode 100644 index 00000000..08fcea1b --- /dev/null +++ b/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/clusterrole.yaml @@ -0,0 +1,29 @@ +{{- if and (or .Values.clustermesh.mcsapi.enabled .Values.clustermesh.enableMCSAPISupport) .Values.clustermesh.mcsapi.corednsAutoConfigure.enabled }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: coredns-mcsapi + labels: + app.kubernetes.io/part-of: cilium + {{- with .Values.commonLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} + annotations: + {{/* + We have to leave CoreDNS RBAC to be able to read MCS-API resources + as we would leave a broken CoreDNS installation otherwise + */}} + helm.sh/resource-policy: keep + {{- with .Values.clustermesh.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +rules: +- apiGroups: + - multicluster.x-k8s.io + resources: + - serviceimports + verbs: + - list + - watch +{{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/clusterrolebinding.yaml b/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/clusterrolebinding.yaml new file mode 100644 index 00000000..7a1867f5 --- /dev/null +++ b/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/clusterrolebinding.yaml @@ -0,0 +1,28 @@ +{{- if and (or .Values.clustermesh.mcsapi.enabled .Values.clustermesh.enableMCSAPISupport) .Values.clustermesh.mcsapi.corednsAutoConfigure.enabled }} +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: coredns-mcsapi + labels: + app.kubernetes.io/part-of: cilium + {{- with .Values.commonLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} + annotations: + {{/* + We have to leave CoreDNS RBAC to be able to read MCS-API resources + as we would leave a broken CoreDNS installation otherwise + */}} + helm.sh/resource-policy: keep + {{- with .Values.clustermesh.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: coredns-mcsapi +subjects: +- kind: ServiceAccount + name: {{ .Values.clustermesh.mcsapi.corednsAutoConfigure.coredns.serviceAccountName | quote }} + namespace: {{ .Values.clustermesh.mcsapi.corednsAutoConfigure.coredns.namespace | quote }} +{{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/job-clusterrole.yaml b/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/job-clusterrole.yaml new file mode 100644 index 00000000..ac339f9f --- /dev/null +++ b/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/job-clusterrole.yaml @@ -0,0 +1,22 @@ +{{- if and (or .Values.clustermesh.mcsapi.enabled .Values.clustermesh.enableMCSAPISupport) .Values.clustermesh.mcsapi.corednsAutoConfigure.enabled }} +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: cilium-coredns-mcsapi-autoconfig + {{- with .Values.commonLabels }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.clustermesh.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +rules: +# note: namespaces permission are needed to initialize and verify that the kubernetes client works. +- apiGroups: + - "" + resources: + - "namespaces" + verbs: + - "get" +{{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/job-clusterrolebinding.yaml b/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/job-clusterrolebinding.yaml new file mode 100644 index 00000000..4b70ccf4 --- /dev/null +++ b/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/job-clusterrolebinding.yaml @@ -0,0 +1,22 @@ +{{- if and (or .Values.clustermesh.mcsapi.enabled .Values.clustermesh.enableMCSAPISupport) .Values.clustermesh.mcsapi.corednsAutoConfigure.enabled }} +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: cilium-coredns-mcsapi-autoconfig + {{- with .Values.commonLabels }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.clustermesh.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cilium-coredns-mcsapi-autoconfig +subjects: +- kind: ServiceAccount + name: {{ .Values.serviceAccounts.corednsMCSAPI.name | quote }} + namespace: {{ include "cilium.namespace" . }} +{{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/job-role.yaml b/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/job-role.yaml new file mode 100644 index 00000000..52f42fae --- /dev/null +++ b/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/job-role.yaml @@ -0,0 +1,36 @@ +{{- if and (or .Values.clustermesh.mcsapi.enabled .Values.clustermesh.enableMCSAPISupport) .Values.clustermesh.mcsapi.corednsAutoConfigure.enabled }} +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: cilium-coredns-mcsapi-autoconfig + namespace: {{ .Values.clustermesh.mcsapi.corednsAutoConfigure.coredns.namespace }} + {{- with .Values.commonLabels }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.clustermesh.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +rules: +- apiGroups: + - "" + resources: + - "configmaps" + verbs: + - "update" + - "patch" + - "get" + resourceNames: + - "{{ .Values.clustermesh.mcsapi.corednsAutoConfigure.coredns.configMapName }}" +- apiGroups: + - "apps" + resources: + - "deployments" + verbs: + - "patch" + - "update" + - "get" + resourceNames: + - "{{ .Values.clustermesh.mcsapi.corednsAutoConfigure.coredns.deploymentName }}" +{{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/job-rolebinding.yaml b/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/job-rolebinding.yaml new file mode 100644 index 00000000..f5caf1a1 --- /dev/null +++ b/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/job-rolebinding.yaml @@ -0,0 +1,23 @@ +{{- if and (or .Values.clustermesh.mcsapi.enabled .Values.clustermesh.enableMCSAPISupport) .Values.clustermesh.mcsapi.corednsAutoConfigure.enabled }} +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: cilium-coredns-mcsapi-autoconfig + namespace: {{ .Values.clustermesh.mcsapi.corednsAutoConfigure.coredns.namespace }} + {{- with .Values.commonLabels }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.clustermesh.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: cilium-coredns-mcsapi-autoconfig +subjects: +- kind: ServiceAccount + name: {{ .Values.serviceAccounts.corednsMCSAPI.name | quote }} + namespace: {{ include "cilium.namespace" . }} +{{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/job-serviceaccount.yaml b/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/job-serviceaccount.yaml new file mode 100644 index 00000000..4e572a13 --- /dev/null +++ b/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/job-serviceaccount.yaml @@ -0,0 +1,20 @@ +{{- if and (or .Values.clustermesh.mcsapi.enabled .Values.clustermesh.enableMCSAPISupport) .Values.clustermesh.mcsapi.corednsAutoConfigure.enabled .Values.serviceAccounts.corednsMCSAPI.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Values.serviceAccounts.corednsMCSAPI.name | quote }} + namespace: {{ include "cilium.namespace" . }} + {{- with .Values.commonLabels }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- if or .Values.serviceAccounts.corednsMCSAPI.annotations .Values.clustermesh.annotations }} + annotations: + {{- with .Values.clustermesh.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.serviceAccounts.corednsMCSAPI.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/job.yaml b/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/job.yaml new file mode 100644 index 00000000..9f9c3ccd --- /dev/null +++ b/packages/system/cilium/charts/cilium/templates/clustermesh-coredns-mcsapi/job.yaml @@ -0,0 +1,82 @@ +{{- if and (or .Values.clustermesh.mcsapi.enabled .Values.clustermesh.enableMCSAPISupport) .Values.clustermesh.mcsapi.corednsAutoConfigure.enabled }} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: cilium-coredns-mcsapi-autoconfig + namespace: {{ include "cilium.namespace" . }} + labels: + k8s-app: cilium-coredns-mcsapi-autoconfig + {{- with .Values.commonLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} + app.kubernetes.io/part-of: cilium + annotations: + "helm.sh/hook": post-install,post-upgrade + {{- with .Values.clustermesh.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.clustermesh.mcsapi.corednsAutoConfigure.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + template: + metadata: + labels: + k8s-app: cilium-coredns-mcsapi-autoconfig + {{- with .Values.clustermesh.mcsapi.corednsAutoConfigure.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + containers: + - name: autoconfig + image: {{ include "cilium.image" .Values.clustermesh.apiserver.image | quote }} + imagePullPolicy: {{ .Values.clustermesh.apiserver.image.pullPolicy }} + {{- with .Values.clustermesh.mcsapi.corednsAutoConfigure.resources }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} + command: + - /usr/bin/clustermesh-apiserver + args: + - mcsapi-coredns-cfg + - --coredns-deployment-name={{ .Values.clustermesh.mcsapi.corednsAutoConfigure.coredns.deploymentName }} + - --coredns-configmap-name={{ .Values.clustermesh.mcsapi.corednsAutoConfigure.coredns.configMapName }} + - --coredns-namespace={{ .Values.clustermesh.mcsapi.corednsAutoConfigure.coredns.namespace }} + - --coredns-cluster-domain={{ .Values.clustermesh.mcsapi.corednsAutoConfigure.coredns.clusterDomain }} + - --coredns-clusterset-domain={{ .Values.clustermesh.mcsapi.corednsAutoConfigure.coredns.clustersetDomain }} + {{- with .Values.clustermesh.mcsapi.corednsAutoConfigure.extraArgs }} + {{- toYaml . | trim | nindent 12 }} + {{- end }} + {{- with .Values.clustermesh.mcsapi.corednsAutoConfigure.extraVolumeMounts }} + volumeMounts: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.clustermesh.mcsapi.corednsAutoConfigure.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.clustermesh.mcsapi.corednsAutoConfigure.priorityClassName }} + priorityClassName: {{ .Values.clustermesh.mcsapi.corednsAutoConfigure.priorityClassName }} + {{- end }} + {{- with .Values.clustermesh.mcsapi.corednsAutoConfigure.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ .Values.serviceAccounts.corednsMCSAPI.name | quote }} + automountServiceAccountToken: true + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + restartPolicy: OnFailure + {{- with .Values.clustermesh.mcsapi.corednsAutoConfigure.extraVolumes }} + volumes: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .Values.clustermesh.mcsapi.corednsAutoConfigure.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + ttlSecondsAfterFinished: {{ .Values.clustermesh.mcsapi.corednsAutoConfigure.ttlSecondsAfterFinished }} +{{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/hubble-relay/configmap.yaml b/packages/system/cilium/charts/cilium/templates/hubble-relay/configmap.yaml index 26b6219a..9df77bae 100644 --- a/packages/system/cilium/charts/cilium/templates/hubble-relay/configmap.yaml +++ b/packages/system/cilium/charts/cilium/templates/hubble-relay/configmap.yaml @@ -29,6 +29,8 @@ data: pprof: {{ .Values.hubble.relay.pprof.enabled | quote }} pprof-address: {{ .Values.hubble.relay.pprof.address | quote }} pprof-port: {{ .Values.hubble.relay.pprof.port | quote }} + pprof-mutex-profile-fraction: {{ .Values.hubble.relay.pprof.mutexProfileFraction | quote }} + pprof-block-profile-rate: {{ .Values.hubble.relay.pprof.blockProfileRate | quote }} {{- end }} {{- if .Values.hubble.relay.prometheus.enabled }} metrics-listen-address: ":{{ .Values.hubble.relay.prometheus.port }}" @@ -44,4 +46,10 @@ data: disable-client-tls: true {{- end }} {{- include "hubble-relay.config.tls" . | nindent 4 }} + {{- if .Values.hubble.relay.logOptions.format }} + log-format: {{ .Values.hubble.relay.logOptions.format }} + {{- end }} + {{- if .Values.hubble.relay.logOptions.level }} + log-level: {{ .Values.hubble.relay.logOptions.level }} + {{- end }} {{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/hubble-ui/clusterrole.yaml b/packages/system/cilium/charts/cilium/templates/hubble-ui/clusterrole.yaml index b8607bd9..ebb8cbd6 100644 --- a/packages/system/cilium/charts/cilium/templates/hubble-ui/clusterrole.yaml +++ b/packages/system/cilium/charts/cilium/templates/hubble-ui/clusterrole.yaml @@ -14,14 +14,6 @@ metadata: {{- end }} rules: -- apiGroups: - - networking.k8s.io - resources: - - networkpolicies - verbs: - - get - - list - - watch - apiGroups: - "" resources: @@ -43,12 +35,4 @@ rules: - get - list - watch -- apiGroups: - - cilium.io - resources: - - "*" - verbs: - - get - - list - - watch {{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/hubble-ui/deployment.yaml b/packages/system/cilium/charts/cilium/templates/hubble-ui/deployment.yaml index c3b3dc5a..07a94dce 100644 --- a/packages/system/cilium/charts/cilium/templates/hubble-ui/deployment.yaml +++ b/packages/system/cilium/charts/cilium/templates/hubble-ui/deployment.yaml @@ -74,11 +74,11 @@ spec: livenessProbe: httpGet: path: /healthz - port: 8081 + port: http readinessProbe: httpGet: path: / - port: 8081 + port: http {{- with .Values.hubble.ui.frontend.resources }} resources: {{- toYaml . | trim | nindent 10 }} @@ -184,8 +184,12 @@ spec: defaultMode: 420 name: hubble-ui-nginx name: hubble-ui-nginx-conf - - emptyDir: {} - name: tmp-dir + - name: tmp-dir + {{- if .Values.hubble.ui.tmpVolume }} + {{- toYaml .Values.hubble.ui.tmpVolume | nindent 8 }} + {{- else }} + emptyDir: {} + {{- end }} {{- if .Values.hubble.relay.tls.server.enabled }} - name: hubble-ui-client-certs {{- if .Values.hubble.ui.standalone.enabled }} diff --git a/packages/system/cilium/charts/cilium/templates/hubble/tls-cronjob/_job-spec.tpl b/packages/system/cilium/charts/cilium/templates/hubble/tls-cronjob/_job-spec.tpl index 72a1f3d8..06fb3347 100644 --- a/packages/system/cilium/charts/cilium/templates/hubble/tls-cronjob/_job-spec.tpl +++ b/packages/system/cilium/charts/cilium/templates/hubble/tls-cronjob/_job-spec.tpl @@ -137,7 +137,6 @@ spec: tolerations: {{- toYaml . | nindent 8 }} {{- end }} - serviceAccount: {{ .Values.serviceAccounts.hubblecertgen.name | quote }} serviceAccountName: {{ .Values.serviceAccounts.hubblecertgen.name | quote }} automountServiceAccountToken: {{ .Values.serviceAccounts.hubblecertgen.automount }} {{- with .Values.imagePullSecrets }} @@ -149,9 +148,11 @@ spec: volumes: {{- toYaml . | nindent 6 }} {{- end }} - affinity: {{- with .Values.certgen.affinity }} + affinity: {{- toYaml . | nindent 8 }} {{- end }} - ttlSecondsAfterFinished: {{ .Values.certgen.ttlSecondsAfterFinished }} + {{- with .Values.certgen.ttlSecondsAfterFinished }} + ttlSecondsAfterFinished: {{ . }} + {{- end }} {{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/hubble/tls-cronjob/cronjob.yaml b/packages/system/cilium/charts/cilium/templates/hubble/tls-cronjob/cronjob.yaml index 697806c6..b49c00a6 100644 --- a/packages/system/cilium/charts/cilium/templates/hubble/tls-cronjob/cronjob.yaml +++ b/packages/system/cilium/charts/cilium/templates/hubble/tls-cronjob/cronjob.yaml @@ -23,6 +23,8 @@ metadata: {{- end }} spec: schedule: {{ .Values.hubble.tls.auto.schedule | quote }} + successfulJobsHistoryLimit: {{ .Values.certgen.cronJob.successfulJobsHistoryLimit }} + failedJobsHistoryLimit: {{ .Values.certgen.cronJob.failedJobsHistoryLimit }} concurrencyPolicy: Forbid jobTemplate: {{- include "hubble-generate-certs.job.spec" . | nindent 4 }} diff --git a/packages/system/cilium/charts/cilium/templates/hubble/tls-cronjob/job.yaml b/packages/system/cilium/charts/cilium/templates/hubble/tls-cronjob/job.yaml index 5e4e67ff..96371916 100644 --- a/packages/system/cilium/charts/cilium/templates/hubble/tls-cronjob/job.yaml +++ b/packages/system/cilium/charts/cilium/templates/hubble/tls-cronjob/job.yaml @@ -1,9 +1,18 @@ {{- if and .Values.hubble.enabled .Values.hubble.tls.enabled .Values.hubble.tls.auto.enabled (eq .Values.hubble.tls.auto.method "cronJob") }} +{{/* +Because Kubernetes job specs are immutable, Helm will fail patch this job if +the spec changes between releases. To avoid breaking the upgrade path, we +generate a name for the job here which is based on the checksum of the spec. +This will cause the name of the job to change if its content changes, +and in turn cause Helm to do delete the old job and replace it with a new one. +*/}} +{{- $jobSpec := include "hubble-generate-certs.job.spec" . -}} +{{- $checkSum := $jobSpec | sha256sum | trunc 10 -}} --- apiVersion: batch/v1 kind: Job metadata: - name: hubble-generate-certs + name: hubble-generate-certs-{{$checkSum}} namespace: {{ include "cilium.namespace" . }} labels: k8s-app: hubble-generate-certs @@ -12,13 +21,14 @@ metadata: {{- with .Values.commonLabels }} {{- toYaml . | nindent 4 }} {{- end }} + {{- if or .Values.certgen.annotations.job .Values.hubble.annotations }} annotations: - "helm.sh/hook": post-install,post-upgrade {{- with .Values.certgen.annotations.job }} - {{- toYaml . | nindent 4 }} + {{- toYaml . | nindent 4 }} {{- end }} {{- with .Values.hubble.annotations }} - {{- toYaml . | nindent 4 }} + {{- toYaml . | nindent 4 }} {{- end }} -{{ include "hubble-generate-certs.job.spec" . }} + {{- end }} +{{ $jobSpec }} {{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/hubble/tls-helm/metrics-server-secret.yaml b/packages/system/cilium/charts/cilium/templates/hubble/tls-helm/metrics-server-secret.yaml index 0cc13efa..d334b986 100644 --- a/packages/system/cilium/charts/cilium/templates/hubble/tls-helm/metrics-server-secret.yaml +++ b/packages/system/cilium/charts/cilium/templates/hubble/tls-helm/metrics-server-secret.yaml @@ -10,13 +10,17 @@ kind: Secret metadata: name: hubble-metrics-server-certs namespace: {{ include "cilium.namespace" . }} - {{- with .Values.commonLabels }} labels: + {{- with .Values.commonLabels }} {{- toYaml . | nindent 4 }} {{- end }} + cilium.io/helm-template-non-idempotent: "true" - {{- with .Values.hubble.annotations }} annotations: + {{- with .Values.hubble.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.nonIdempotentAnnotations }} {{- toYaml . | nindent 4 }} {{- end }} type: kubernetes.io/tls diff --git a/packages/system/cilium/charts/cilium/templates/hubble/tls-helm/relay-client-secret.yaml b/packages/system/cilium/charts/cilium/templates/hubble/tls-helm/relay-client-secret.yaml index fa0c908e..6f001dc9 100644 --- a/packages/system/cilium/charts/cilium/templates/hubble/tls-helm/relay-client-secret.yaml +++ b/packages/system/cilium/charts/cilium/templates/hubble/tls-helm/relay-client-secret.yaml @@ -17,13 +17,17 @@ kind: Secret metadata: name: hubble-relay-client-certs namespace: {{ include "cilium.namespace" . }} - {{- with .Values.commonLabels }} labels: + {{- with .Values.commonLabels }} {{- toYaml . | nindent 4 }} {{- end }} + cilium.io/helm-template-non-idempotent: "true" - {{- with .Values.hubble.annotations }} annotations: + {{- with .Values.hubble.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.nonIdempotentAnnotations }} {{- toYaml . | nindent 4 }} {{- end }} type: kubernetes.io/tls diff --git a/packages/system/cilium/charts/cilium/templates/hubble/tls-helm/relay-server-secret.yaml b/packages/system/cilium/charts/cilium/templates/hubble/tls-helm/relay-server-secret.yaml index 986b3cac..229148a0 100644 --- a/packages/system/cilium/charts/cilium/templates/hubble/tls-helm/relay-server-secret.yaml +++ b/packages/system/cilium/charts/cilium/templates/hubble/tls-helm/relay-server-secret.yaml @@ -10,13 +10,17 @@ kind: Secret metadata: name: hubble-relay-server-certs namespace: {{ include "cilium.namespace" . }} - {{- with .Values.commonLabels }} labels: + {{- with .Values.commonLabels }} {{- toYaml . | nindent 4 }} {{- end }} + cilium.io/helm-template-non-idempotent: "true" - {{- with .Values.hubble.annotations }} annotations: + {{- with .Values.hubble.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.nonIdempotentAnnotations }} {{- toYaml . | nindent 4 }} {{- end }} type: kubernetes.io/tls diff --git a/packages/system/cilium/charts/cilium/templates/hubble/tls-helm/server-secret.yaml b/packages/system/cilium/charts/cilium/templates/hubble/tls-helm/server-secret.yaml index b7bedb89..4d214a56 100644 --- a/packages/system/cilium/charts/cilium/templates/hubble/tls-helm/server-secret.yaml +++ b/packages/system/cilium/charts/cilium/templates/hubble/tls-helm/server-secret.yaml @@ -18,13 +18,17 @@ kind: Secret metadata: name: hubble-server-certs namespace: {{ include "cilium.namespace" . }} - {{- with .Values.commonLabels }} labels: + {{- with .Values.commonLabels }} {{- toYaml . | nindent 4 }} {{- end }} + cilium.io/helm-template-non-idempotent: "true" - {{- with .Values.hubble.annotations }} annotations: + {{- with .Values.hubble.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.nonIdempotentAnnotations }} {{- toYaml . | nindent 4 }} {{- end }} type: kubernetes.io/tls diff --git a/packages/system/cilium/charts/cilium/templates/hubble/tls-helm/ui-client-certs.yaml b/packages/system/cilium/charts/cilium/templates/hubble/tls-helm/ui-client-certs.yaml index e1f62ead..8c1b40aa 100644 --- a/packages/system/cilium/charts/cilium/templates/hubble/tls-helm/ui-client-certs.yaml +++ b/packages/system/cilium/charts/cilium/templates/hubble/tls-helm/ui-client-certs.yaml @@ -10,13 +10,17 @@ metadata: name: hubble-ui-client-certs namespace: {{ include "cilium.namespace" . }} - {{- with .Values.commonLabels }} labels: + {{- with .Values.commonLabels }} {{- toYaml . | nindent 4 }} {{- end }} + cilium.io/helm-template-non-idempotent: "true" - {{- with .Values.hubble.annotations }} annotations: + {{- with .Values.hubble.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.nonIdempotentAnnotations }} {{- toYaml . | nindent 4 }} {{- end }} type: kubernetes.io/tls diff --git a/packages/system/cilium/charts/cilium/templates/standalone-dns-proxy/configmap.yaml b/packages/system/cilium/charts/cilium/templates/standalone-dns-proxy/configmap.yaml new file mode 100644 index 00000000..9d65c1a8 --- /dev/null +++ b/packages/system/cilium/charts/cilium/templates/standalone-dns-proxy/configmap.yaml @@ -0,0 +1,28 @@ +{{- if .Values.standaloneDnsProxy.enabled }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: standalone-dns-proxy-config + namespace: {{ include "cilium.namespace" . }} + {{- with .Values.commonLabels }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + + {{- with .Values.standaloneDnsProxy.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +data: + # Use the same L7 proxy and DNS settings as the agent for consistency + enable-l7-proxy: {{ .Values.l7Proxy | quote }} + debug: {{ .Values.standaloneDnsProxy.debug | quote }} + enable-standalone-dns-proxy: {{ .Values.standaloneDnsProxy.enabled | quote }} + enable-ipv4: {{ .Values.ipv4.enabled | quote }} + enable-ipv6: {{ .Values.ipv6.enabled | quote }} + standalone-dns-proxy-server-port: {{ .Values.standaloneDnsProxy.serverPort | quote }} + # DNS proxy configuration inherited from agent settings + tofqdns-proxy-port: {{ .Values.dnsProxy.proxyPort | quote }} + tofqdns-enable-dns-compression: {{ .Values.dnsProxy.enableDnsCompression | quote }} +{{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/standalone-dns-proxy/daemonset.yaml b/packages/system/cilium/charts/cilium/templates/standalone-dns-proxy/daemonset.yaml new file mode 100644 index 00000000..7ea150a4 --- /dev/null +++ b/packages/system/cilium/charts/cilium/templates/standalone-dns-proxy/daemonset.yaml @@ -0,0 +1,80 @@ +{{- if .Values.standaloneDnsProxy.enabled }} +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: standalone-dns-proxy + namespace: {{ include "cilium.namespace" . }} + {{- with .Values.standaloneDnsProxy.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + k8s-app: standalone-dns-proxy + app.kubernetes.io/part-of: cilium + app.kubernetes.io/name: standalone-dns-proxy + name: standalone-dns-proxy + {{- with .Values.commonLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + minReadySeconds: 5 + {{- with .Values.standaloneDnsProxy.updateStrategy }} + updateStrategy: + {{- toYaml . | nindent 4 }} + {{- end }} + selector: + matchLabels: + k8s-app: standalone-dns-proxy + template: + metadata: + annotations: + {{- if .Values.standaloneDnsProxy.rollOutPods }} + # ensure pods roll when configmap updates + cilium.io/standalone-dns-proxy-configmap-checksum: {{ include (print $.Template.BasePath "/standalone-dns-proxy/configmap.yaml") . | sha256sum | quote }} + {{- end }} + container.apparmor.security.beta.kubernetes.io/standalone-dns-proxy: "unconfined" + labels: + k8s-app: standalone-dns-proxy + name: standalone-dns-proxy + app.kubernetes.io/name: standalone-dns-proxy + app.kubernetes.io/part-of: cilium + {{- with .Values.commonLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + hostNetwork: true + automountServiceAccountToken: {{ .Values.standaloneDnsProxy.automountServiceAccountToken }} + {{- with .Values.standaloneDnsProxy.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + tolerations: + - operator: Exists + {{- with .Values.standaloneDnsProxy.tolerations }} + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: standalone-dns-proxy + image: {{ include "cilium.image" .Values.standaloneDnsProxy.image | quote }} + args: + - --config-dir=/tmp/standalone-dns-proxy/config-map + imagePullPolicy: {{ .Values.standaloneDnsProxy.image.pullPolicy }} + volumeMounts: + - mountPath: /tmp/standalone-dns-proxy/config-map + name: standalone-dns-proxy-config-path + readOnly: true + - mountPath: /var/run/standalone-dns-proxy + name: runtime-dir + securityContext: + capabilities: + add: ["NET_ADMIN", "NET_RAW"] + drop: ["ALL"] + volumes: + - configMap: + defaultMode: 420 + name: standalone-dns-proxy-config + name: standalone-dns-proxy-config-path + - emptyDir: {} + name: runtime-dir +{{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/validate.yaml b/packages/system/cilium/charts/cilium/templates/validate.yaml index 4fff21a3..ac3164b6 100644 --- a/packages/system/cilium/charts/cilium/templates/validate.yaml +++ b/packages/system/cilium/charts/cilium/templates/validate.yaml @@ -220,3 +220,22 @@ {{- end }} {{- end }} {{- end }} + +{{/* validate Standalone DNS Proxy */}} +{{- if .Values.standaloneDnsProxy.enabled }} + {{- if not .Values.dnsProxy.proxyPort }} + {{ fail "standaloneDnsProxy requires dnsProxy.proxyPort to be explicitly set (e.g., 10094)" }} + {{- end }} + {{- if eq (int .Values.dnsProxy.proxyPort) 0 }} + {{ fail "standaloneDnsProxy requires dnsProxy.proxyPort to be set to a non-zero value (e.g., 10094). The standalone DNS proxy uses the same DNS configuration as the agent." }} + {{- end }} +{{- end }} + +{{/* validate we don't run tproxy with netkit - see GH issue 39892 */}} +{{- if hasKey .Values "bpf" }} + {{- if and (hasKey .Values.bpf "tproxy") (hasKey .Values.bpf "datapathMode") }} + {{- if and (.Values.bpf.tproxy) (list "netkit" "netkit-l2" | has .Values.bpf.datapathMode) }} + {{ fail ".Values.bpf.tproxy cannot be enabled with .Values.bpf.datapathMode=netkit or .Values.bpf.datapathMode=netkit-l2" }} + {{- end }} + {{- end }} +{{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/ztunnel/daemonset.yaml b/packages/system/cilium/charts/cilium/templates/ztunnel/daemonset.yaml new file mode 100644 index 00000000..f9363068 --- /dev/null +++ b/packages/system/cilium/charts/cilium/templates/ztunnel/daemonset.yaml @@ -0,0 +1,165 @@ +{{- if and .Values.encryption.enabled (eq .Values.encryption.type "ztunnel") }} +--- +kind: DaemonSet +apiVersion: apps/v1 +metadata: + name: ztunnel-cilium + namespace: {{ include "cilium.namespace" . }} + {{- with .Values.encryption.ztunnel.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + app: ztunnel-cilium + app.kubernetes.io/part-of: cilium + app.kubernetes.io/name: ztunnel-cilium + {{- with .Values.commonLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + revisionHistoryLimit: 10 + selector: + matchLabels: + app: ztunnel-cilium + {{- with .Values.encryption.ztunnel.updateStrategy }} + updateStrategy: + {{- toYaml . | trim | nindent 4 }} + {{- end }} + template: + metadata: + annotations: + sidecar.istio.io/inject: "false" + {{- with .Values.encryption.ztunnel.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + app: ztunnel-cilium + app.kubernetes.io/part-of: cilium + app.kubernetes.io/name: ztunnel-cilium + {{- with .Values.commonLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.encryption.ztunnel.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + hostNetwork: true + dnsPolicy: ClusterFirst + containers: + - name: istio-proxy + image: {{ include "cilium.image" .Values.encryption.ztunnel.image | quote }} + imagePullPolicy: {{ .Values.encryption.ztunnel.image.pullPolicy }} + args: + - proxy + - ztunnel + env: + - name: XDS_ADDRESS + value: "https://localhost:15012" + - name: XDS_ROOT_CA + value: "/etc/ztunnel/bootstrap-root.crt" + - name: CA_ROOT_CA + value: "/etc/ztunnel/bootstrap-root.crt" + - name: CA_ADDRESS + value: {{ .Values.encryption.ztunnel.caAddress | quote }} + - name: ISTIO_META_DNS_CAPTURE + value: "false" + - name: INPOD_UDS + value: "/var/run/cilium/ztunnel.sock" + - name: NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: status.hostIP + - name: INSTANCE_IP + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: status.podIP + - name: ISTIO_META_ENABLE_HBONE + value: "true" + {{- with .Values.encryption.ztunnel.extraEnv }} + {{- toYaml . | trim | nindent 12 }} + {{- end }} + readinessProbe: + httpGet: + path: /healthz/ready + port: {{ .Values.encryption.ztunnel.healthPort }} + host: "127.0.0.1" + scheme: HTTP + initialDelaySeconds: {{ .Values.encryption.ztunnel.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.encryption.ztunnel.readinessProbe.periodSeconds }} + failureThreshold: {{ .Values.encryption.ztunnel.readinessProbe.failureThreshold }} + successThreshold: 1 + timeoutSeconds: 1 + {{- with .Values.encryption.ztunnel.resources }} + resources: + {{- toYaml . | trim | nindent 12 }} + {{- end }} + securityContext: + allowPrivilegeEscalation: true + capabilities: + add: + - NET_ADMIN + - SYS_ADMIN + - NET_RAW + drop: + - ALL + privileged: false + readOnlyRootFilesystem: true + runAsGroup: 1337 + runAsNonRoot: false + runAsUser: 0 + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /var/run/cilium + name: cilium-dir + readOnly: false + - mountPath: /etc/ztunnel + name: cilium-ztunnel-secrets + readOnly: true + {{- with .Values.encryption.ztunnel.extraVolumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.encryption.ztunnel.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.encryption.ztunnel.nodeSelector }} + nodeSelector: + {{- toYaml . | trim | nindent 8 }} + {{- end }} + {{- with .Values.encryption.ztunnel.tolerations }} + tolerations: + {{- toYaml . | trim | nindent 8 }} + {{- end }} + priorityClassName: {{ include "cilium.priorityClass" (list $ .Values.encryption.ztunnel.priorityClassName "system-node-critical") }} + restartPolicy: Always + terminationGracePeriodSeconds: {{ .Values.encryption.ztunnel.terminationGracePeriodSeconds }} + {{- if .Values.serviceAccounts.ztunnel.create }} + serviceAccountName: {{ .Values.serviceAccounts.ztunnel.name | quote }} + automountServiceAccountToken: {{ .Values.serviceAccounts.ztunnel.automount }} + {{- else }} + automountServiceAccountToken: false + {{- end }} + volumes: + - name: cilium-dir + hostPath: + path: /var/run/cilium + type: DirectoryOrCreate + - name: cilium-ztunnel-secrets + secret: + secretName: cilium-ztunnel-secrets + defaultMode: 420 + items: + - key: bootstrap-root.crt + path: bootstrap-root.crt + mode: 420 + {{- with .Values.encryption.ztunnel.extraVolumes }} + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/ztunnel/secret.yaml b/packages/system/cilium/charts/cilium/templates/ztunnel/secret.yaml new file mode 100644 index 00000000..520857dc --- /dev/null +++ b/packages/system/cilium/charts/cilium/templates/ztunnel/secret.yaml @@ -0,0 +1,23 @@ +{{- if and .Values.encryption.enabled (eq .Values.encryption.type "ztunnel") }} +{{- if .Values.encryption.ztunnel.secrets.bootstrapRootCert }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: cilium-ztunnel-secrets + namespace: {{ include "cilium.namespace" . }} + {{- with .Values.encryption.ztunnel.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + app.kubernetes.io/part-of: cilium + app.kubernetes.io/name: ztunnel-cilium + {{- with .Values.commonLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} +type: Opaque +data: + bootstrap-root.crt: {{ .Values.encryption.ztunnel.secrets.bootstrapRootCert | b64enc }} +{{- end }} +{{- end }} diff --git a/packages/system/cilium/charts/cilium/templates/ztunnel/serviceaccount.yaml b/packages/system/cilium/charts/cilium/templates/ztunnel/serviceaccount.yaml new file mode 100644 index 00000000..4ef9e2bc --- /dev/null +++ b/packages/system/cilium/charts/cilium/templates/ztunnel/serviceaccount.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.encryption.enabled (eq .Values.encryption.type "ztunnel") .Values.serviceAccounts.ztunnel.create }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Values.serviceAccounts.ztunnel.name | quote }} + namespace: {{ include "cilium.namespace" . }} + {{- with .Values.commonLabels }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- if or .Values.serviceAccounts.ztunnel.annotations .Values.encryption.ztunnel.annotations }} + annotations: + {{- with .Values.encryption.ztunnel.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.serviceAccounts.ztunnel.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/packages/system/cilium/charts/cilium/values.schema.json b/packages/system/cilium/charts/cilium/values.schema.json index fb1ea4c3..65c84af5 100644 --- a/packages/system/cilium/charts/cilium/values.schema.json +++ b/packages/system/cilium/charts/cilium/values.schema.json @@ -58,6 +58,27 @@ "properties": { "enabled": { "type": "boolean" + }, + "nodeSpec": { + "properties": { + "securityGroupTags": { + "items": {}, + "type": "array" + }, + "securityGroups": { + "items": {}, + "type": "array" + }, + "vSwitchTags": { + "items": {}, + "type": "array" + }, + "vSwitches": { + "items": {}, + "type": "array" + } + }, + "type": "object" } }, "type": "object" @@ -440,6 +461,14 @@ "properties": { "enabled": { "type": "boolean" + }, + "nodeSpec": { + "properties": { + "azureInterfaceName": { + "type": "string" + } + }, + "type": "object" } }, "type": "object" @@ -644,6 +673,14 @@ "monitorInterval": { "type": "string" }, + "monitorTraceIPOption": { + "minimum": 0, + "maximum": 255, + "type": [ + "null", + "integer" + ] + }, "natMax": { "type": [ "null", @@ -668,6 +705,12 @@ "integer" ] }, + "policyMapPressureMetricsThreshold": { + "type": [ + "null", + "number" + ] + }, "policyStatsMapMax": { "type": [ "null", @@ -714,6 +757,17 @@ }, "type": "object" }, + "cronJob": { + "properties": { + "failedJobsHistoryLimit": { + "type": "integer" + }, + "successfulJobsHistoryLimit": { + "type": "integer" + } + }, + "type": "object" + }, "extraVolumeMounts": { "items": {}, "type": "array" @@ -768,7 +822,10 @@ "type": "array" }, "ttlSecondsAfterFinished": { - "type": "integer" + "type": [ + "null", + "integer" + ] } }, "type": "object" @@ -1299,6 +1356,9 @@ "Cluster" ] }, + "externallyCreated": { + "type": "boolean" + }, "internalTrafficPolicy": { "enum": [ "Local", @@ -1369,17 +1429,6 @@ }, "type": "object" }, - "client": { - "properties": { - "cert": { - "type": "string" - }, - "key": { - "type": "string" - } - }, - "type": "object" - }, "enableSecrets": { "type": "boolean" }, @@ -1452,11 +1501,17 @@ }, "type": "object" }, + "cacheTTL": { + "type": "string" + }, "config": { "properties": { "clusters": { "items": {}, - "type": "array" + "type": [ + "object", + "array" + ] }, "domain": { "type": "string" @@ -1476,6 +1531,85 @@ "maxConnectedClusters": { "type": "integer" }, + "mcsapi": { + "properties": { + "corednsAutoConfigure": { + "properties": { + "affinity": { + "type": "object" + }, + "annotations": { + "type": "object" + }, + "coredns": { + "properties": { + "clusterDomain": { + "type": "string" + }, + "clustersetDomain": { + "type": "string" + }, + "configMapName": { + "type": "string" + }, + "deploymentName": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "serviceAccountName": { + "type": "string" + } + }, + "type": "object" + }, + "enabled": { + "type": "boolean" + }, + "extraArgs": { + "items": {}, + "type": "array" + }, + "extraVolumeMounts": { + "items": {}, + "type": "array" + }, + "extraVolumes": { + "items": {}, + "type": "array" + }, + "nodeSelector": { + "type": "object" + }, + "podLabels": { + "type": "object" + }, + "priorityClassName": { + "type": "string" + }, + "resources": { + "type": "object" + }, + "tolerations": { + "items": {}, + "type": "array" + }, + "ttlSecondsAfterFinished": { + "type": "integer" + } + }, + "type": "object" + }, + "enabled": { + "type": "boolean" + }, + "installCRDs": { + "type": "boolean" + } + }, + "type": "object" + }, "policyDefaultLocalCluster": { "type": "boolean" }, @@ -1537,10 +1671,27 @@ }, "resources": { "properties": { + "limits": { + "properties": { + "cpu": { + "type": [ + "integer", + "string" + ] + }, + "memory": { + "type": "string" + } + }, + "type": "object" + }, "requests": { "properties": { "cpu": { - "type": "string" + "type": [ + "integer", + "string" + ] }, "memory": { "type": "string" @@ -1563,6 +1714,21 @@ "object" ] }, + "configDriftDetection": { + "properties": { + "driftChecker": { + "type": "boolean" + }, + "enabled": { + "type": "boolean" + }, + "ignoredKeys": { + "items": {}, + "type": "array" + } + }, + "type": "object" + }, "connectivityProbeFrequencyRatio": { "type": [ "null", @@ -1578,14 +1744,6 @@ "crdWaitTimeout": { "type": "string" }, - "customCalls": { - "properties": { - "enabled": { - "type": "boolean" - } - }, - "type": "object" - }, "daemon": { "properties": { "allowedConfigOverrides": { @@ -1749,6 +1907,9 @@ "enableMasqueradeRouteSource": { "type": "boolean" }, + "enableNoServiceEndpointsRoutable": { + "type": "boolean" + }, "enableNonDefaultDenyPolicies": { "type": "boolean" }, @@ -1797,8 +1958,30 @@ "cidr": { "type": "string" }, + "egress": { + "properties": { + "allowRemoteNodeIdentities": { + "type": "boolean" + }, + "cidr": { + "type": "string" + }, + "enabled": { + "type": "boolean" + } + }, + "type": "object" + }, "enabled": { "type": "boolean" + }, + "ingress": { + "properties": { + "enabled": { + "type": "boolean" + } + }, + "type": "object" } }, "type": "object" @@ -1813,6 +1996,184 @@ } }, "type": "object" + }, + "ztunnel": { + "properties": { + "affinity": { + "type": "object" + }, + "annotations": { + "type": "object" + }, + "caAddress": { + "type": "string" + }, + "extraEnv": { + "items": {}, + "type": "array" + }, + "extraVolumeMounts": { + "items": {}, + "type": "array" + }, + "extraVolumes": { + "items": {}, + "type": "array" + }, + "healthPort": { + "type": "integer" + }, + "image": { + "properties": { + "digest": { + "type": [ + "null", + "string" + ] + }, + "override": { + "type": [ + "null", + "string" + ] + }, + "pullPolicy": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "useDigest": { + "type": "boolean" + } + }, + "type": "object" + }, + "nodeSelector": { + "properties": { + "kubernetes.io/os": { + "type": "string" + } + }, + "type": "object" + }, + "podAnnotations": { + "type": "object" + }, + "podLabels": { + "type": "object" + }, + "priorityClassName": { + "type": [ + "null", + "string" + ] + }, + "readinessProbe": { + "properties": { + "failureThreshold": { + "type": "integer" + }, + "initialDelaySeconds": { + "type": "integer" + }, + "periodSeconds": { + "type": "integer" + } + }, + "type": "object" + }, + "resources": { + "properties": { + "requests": { + "properties": { + "cpu": { + "type": "string" + }, + "memory": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "secrets": { + "properties": { + "bootstrapRootCert": { + "type": [ + "null", + "string" + ] + } + }, + "type": "object" + }, + "terminationGracePeriodSeconds": { + "type": "integer" + }, + "tolerations": { + "items": { + "anyOf": [ + { + "properties": { + "effect": { + "type": "string" + }, + "operator": { + "type": "string" + } + } + }, + { + "properties": { + "key": { + "type": "string" + }, + "operator": { + "type": "string" + } + } + }, + { + "properties": { + "effect": { + "type": "string" + }, + "operator": { + "type": "string" + } + } + } + ] + }, + "type": "array" + }, + "updateStrategy": { + "properties": { + "rollingUpdate": { + "properties": { + "maxSurge": { + "type": "integer" + }, + "maxUnavailable": { + "type": "integer" + } + }, + "type": "object" + }, + "type": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" } }, "type": "object" @@ -1828,6 +2189,9 @@ "endpointLockdownOnMapOverflow": { "type": "boolean" }, + "endpointPolicyUpdateTimeoutDuration": { + "type": "null" + }, "endpointRoutes": { "properties": { "enabled": { @@ -1869,6 +2233,49 @@ "items": {}, "type": "array" }, + "nodeSpec": { + "properties": { + "deleteOnTermination": { + "type": [ + "null", + "boolean" + ] + }, + "disablePrefixDelegation": { + "type": "boolean" + }, + "excludeInterfaceTags": { + "items": {}, + "type": "array" + }, + "firstInterfaceIndex": { + "type": [ + "null", + "integer" + ] + }, + "securityGroupTags": { + "items": {}, + "type": "array" + }, + "securityGroups": { + "items": {}, + "type": "array" + }, + "subnetIDs": { + "items": {}, + "type": "array" + }, + "subnetTags": { + "items": {}, + "type": "array" + }, + "usePrimaryAddress": { + "type": "boolean" + } + }, + "type": "object" + }, "subnetIDsFilter": { "items": {}, "type": "array" @@ -2011,6 +2418,12 @@ "string" ] }, + "clusterMaxConnections": { + "type": "integer" + }, + "clusterMaxRequests": { + "type": "integer" + }, "connectTimeoutSeconds": { "type": "integer" }, @@ -2104,6 +2517,10 @@ }, "type": "object" }, + "initContainers": { + "items": {}, + "type": "array" + }, "initialFetchTimeoutSeconds": { "type": "integer" }, @@ -2171,6 +2588,9 @@ "maxConnectionDurationSeconds": { "type": "integer" }, + "maxGlobalDownstreamConnections": { + "type": "integer" + }, "maxRequestsPerConnection": { "type": "integer" }, @@ -2393,6 +2813,9 @@ }, "type": "object" }, + "useOriginalSourceAddress": { + "type": "boolean" + }, "xffNumTrustedHopsL7PolicyEgress": { "type": "integer" }, @@ -2614,10 +3037,17 @@ "anyOf": [ { "properties": { + "aggregationInterval": { + "type": "string" + }, "excludeFilters": { "items": {}, "type": "array" }, + "fieldAggregate": { + "items": {}, + "type": "array" + }, "fieldMask": { "items": {}, "type": "array" @@ -2661,6 +3091,9 @@ }, "static": { "properties": { + "aggregationInterval": { + "type": "string" + }, "allowList": { "items": {}, "type": "array" @@ -2672,6 +3105,10 @@ "enabled": { "type": "boolean" }, + "fieldAggregate": { + "items": {}, + "type": "array" + }, "fieldMask": { "items": {}, "type": "array" @@ -3037,6 +3474,23 @@ "listenPort": { "type": "string" }, + "logOptions": { + "properties": { + "format": { + "type": [ + "null", + "string" + ] + }, + "level": { + "type": [ + "null", + "string" + ] + } + }, + "type": "object" + }, "nodeSelector": { "properties": { "kubernetes.io/os": { @@ -3100,9 +3554,15 @@ "address": { "type": "string" }, + "blockProfileRate": { + "type": "integer" + }, "enabled": { "type": "boolean" }, + "mutexProfileFraction": { + "type": "integer" + }, "port": { "type": "integer" } @@ -3680,6 +4140,9 @@ }, "type": "object" }, + "tmpVolume": { + "type": "object" + }, "tolerations": { "items": {}, "type": "array" @@ -3921,6 +4384,33 @@ "multiPoolPreAllocation": { "type": "string" }, + "nodeSpec": { + "properties": { + "ipamMaxAllocate": { + "type": [ + "null", + "integer" + ] + }, + "ipamMinAllocate": { + "type": [ + "null", + "integer" + ] + }, + "ipamPreAllocate": { + "type": [ + "null", + "integer" + ] + }, + "ipamStaticIPTags": { + "items": {}, + "type": "array" + } + }, + "type": "object" + }, "operator": { "properties": { "autoCreateCiliumPodIPPools": { @@ -4187,6 +4677,9 @@ } }, "type": "object" + }, + "serviceTopology": { + "type": "boolean" } }, "type": "object" @@ -4278,9 +4771,6 @@ }, "enableHealthCheckLoadBalancerIP": { "type": "boolean" - }, - "enabled": { - "type": "boolean" } }, "type": "object" @@ -4394,7 +4884,10 @@ "requests": { "properties": { "cpu": { - "type": "string" + "type": [ + "integer", + "string" + ] }, "memory": { "type": "string" @@ -4486,6 +4979,9 @@ } }, "type": "object" + }, + "waitForCloudInit": { + "type": "boolean" } }, "type": "object" @@ -4694,9 +5190,15 @@ "address": { "type": "string" }, + "blockProfileRate": { + "type": "integer" + }, "enabled": { "type": "boolean" }, + "mutexProfileFraction": { + "type": "integer" + }, "port": { "type": "integer" } @@ -4754,6 +5256,30 @@ } }, "type": "object" + }, + "tls": { + "properties": { + "enabled": { + "type": "boolean" + }, + "server": { + "properties": { + "existingSecret": { + "type": "string" + }, + "mtls": { + "properties": { + "enabled": { + "type": "boolean" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "type": "object" } }, "type": "object" @@ -4866,6 +5392,12 @@ }, "restart": { "type": "boolean" + }, + "selector": { + "type": [ + "null", + "string" + ] } }, "type": "object" @@ -4902,6 +5434,9 @@ "properties": { "enabled": { "type": "boolean" + }, + "packetizationLayerPMTUDMode": { + "type": "string" } }, "type": "object" @@ -4940,6 +5475,9 @@ "array" ] }, + "policyDenyResponse": { + "type": "string" + }, "policyEnforcementMode": { "type": "string" }, @@ -4948,9 +5486,15 @@ "address": { "type": "string" }, + "blockProfileRate": { + "type": "integer" + }, "enabled": { "type": "boolean" }, + "mutexProfileFraction": { + "type": "integer" + }, "port": { "type": "integer" } @@ -5363,6 +5907,9 @@ "secretsNamespaceAnnotations": { "type": "object" }, + "secretsNamespaceLabels": { + "type": "object" + }, "securityContext": { "properties": { "allowPrivilegeEscalation": { @@ -5422,6 +5969,9 @@ { "type": "string" }, + { + "type": "string" + }, { "type": "string" } @@ -5537,6 +6087,23 @@ }, "type": "object" }, + "corednsMCSAPI": { + "properties": { + "annotations": { + "type": "object" + }, + "automount": { + "type": "boolean" + }, + "create": { + "type": "boolean" + }, + "name": { + "type": "string" + } + }, + "type": "object" + }, "envoy": { "properties": { "annotations": { @@ -5658,6 +6225,23 @@ } }, "type": "object" + }, + "ztunnel": { + "properties": { + "annotations": { + "type": "object" + }, + "automount": { + "type": "boolean" + }, + "create": { + "type": "boolean" + }, + "name": { + "type": "string" + } + }, + "type": "object" } }, "type": "object" @@ -5676,6 +6260,86 @@ }, "type": "object" }, + "standaloneDnsProxy": { + "properties": { + "annotations": { + "type": "object" + }, + "automountServiceAccountToken": { + "type": "boolean" + }, + "debug": { + "type": "boolean" + }, + "enabled": { + "type": "boolean" + }, + "image": { + "properties": { + "digest": { + "type": "string" + }, + "override": { + "type": [ + "null", + "string" + ] + }, + "pullPolicy": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "useDigest": { + "type": "boolean" + } + }, + "type": "object" + }, + "nodeSelector": { + "properties": { + "kubernetes.io/os": { + "type": "string" + } + }, + "type": "object" + }, + "rollOutPods": { + "type": "boolean" + }, + "serverPort": { + "type": "integer" + }, + "tolerations": { + "items": {}, + "type": "array" + }, + "updateStrategy": { + "properties": { + "rollingUpdate": { + "properties": { + "maxSurge": { + "type": "integer" + }, + "maxUnavailable": { + "type": "integer" + } + }, + "type": "object" + }, + "type": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + }, "startupProbe": { "properties": { "failureThreshold": { @@ -5687,9 +6351,6 @@ }, "type": "object" }, - "svcSourceRangeCheck": { - "type": "boolean" - }, "synchronizeK8sNodes": { "type": "boolean" }, @@ -5774,6 +6435,9 @@ }, "type": "object" }, + "tmpVolume": { + "type": "object" + }, "tolerations": { "items": { "anyOf": [ diff --git a/packages/system/cilium/charts/cilium/values.yaml b/packages/system/cilium/charts/cilium/values.yaml index eff7f902..66f76982 100644 --- a/packages/system/cilium/charts/cilium/values.yaml +++ b/packages/system/cilium/charts/cilium/values.yaml @@ -6,7 +6,6 @@ # type: [null, string] # @schema # -- namespaceOverride allows to override the destination namespace for Cilium resources. -# This property allows to use Cilium as part of an Umbrella Chart with different targets. namespaceOverride: "" # @schema # type: [null, object] @@ -29,7 +28,7 @@ debug: # @schema # -- Configure verbosity levels for debug logging # This option is used to enable debug messages for operations related to such - # sub-system such as (e.g. kvstore, envoy, datapath or policy), and flow is + # sub-system such as (e.g. kvstore, envoy, datapath, policy, or tagged), and flow is # for enabling debug messages emitted per request, message and connection. # Multiple values can be set via a space-separated string (e.g. "datapath envoy"). # @@ -39,6 +38,7 @@ debug: # - envoy # - datapath # - policy + # - tagged verbose: ~ # -- Set the agent-internal metrics sampling frequency. This sets the # frequency of the internal sampling of the agent metrics. These are @@ -204,6 +204,18 @@ serviceAccounts: name: hubble-generate-certs automount: true annotations: {} + # -- CorednsMCSAPI is used if clustermesh.mcsapi.corednsAutoConfigure.enabled=true + corednsMCSAPI: + create: true + name: cilium-coredns-mcsapi-autoconfig + automount: true + annotations: {} + # -- Ztunnel is used if encryption.type=ztunnel + ztunnel: + create: true + name: ztunnel-cilium + automount: false + annotations: {} # -- Configure termination grace period for cilium-agent DaemonSet. terminationGracePeriodSeconds: 1 # -- Install the cilium agent resources. @@ -212,6 +224,22 @@ agent: true name: cilium # -- Roll out cilium agent pods automatically when configmap is updated. rollOutCiliumPods: false +# -- Configuration for the ConfigMap drift detection feature. +# When enabled, the agent continuously watches the cilium-config ConfigMap +# and exposes a cilium_drift_checker_config_delta Prometheus metric reporting +# the number of keys that differ between the ConfigMap and the agent's active +# settings. A non-zero value indicates that the agent has not yet applied all +# current ConfigMap changes and needs to be restarted. +configDriftDetection: + # -- Enable watching of the cilium-config ConfigMap and reflecting its + # contents into the agent's internal DynamicConfig table. + enabled: true + # -- Enable the drift checker which compares the DynamicConfig table against + # the agent's active settings and publishes the + # cilium_drift_checker_config_delta metric. + driftChecker: true + # -- List of config-map keys to ignore when computing the drift delta. + ignoredKeys: [] # -- Agent container image. image: # @schema @@ -219,10 +247,10 @@ image: # @schema override: ~ repository: "quay.io/cilium/cilium" - tag: "v1.18.6" + tag: "v1.19.3" pullPolicy: "IfNotPresent" # cilium-digest - digest: sha256:42ec562a5ff6c8a860c0639f5a7611685e253fd9eb2d2fcdade693724c9166a4 + digest: sha256:2e61680593cddca8b6c055f6d4c849d87a26a1c91c7e3b8b56c7fb76ab7b7b10 useDigest: true # -- Scheduling configurations for cilium pods scheduling: @@ -363,6 +391,8 @@ securityContext: - SETGID # Allow to execute program that changes UID (e.g. required for package installation) - SETUID + # Allow to read dmesg and get kernel pointers when kptr_restrict=1 + - SYSLOG # -- Capabilities for the `mount-cgroup` init container mountCgroup: # Only used for 'mount' cgroup @@ -433,9 +463,16 @@ azure: # clientID: 00000000-0000-0000-0000-000000000000 # clientSecret: 00000000-0000-0000-0000-000000000000 # userAssignedIdentityID: 00000000-0000-0000-0000-000000000000 + nodeSpec: + azureInterfaceName: "" alibabacloud: # -- Enable AlibabaCloud ENI integration enabled: false + nodeSpec: + vSwitches: [] + vSwitchTags: [] + securityGroups: [] + securityGroupTags: [] # -- Enable bandwidth manager to optimize TCP and UDP workloads and allow # for rate-limiting traffic from individual Pods with EDT (Earliest Departure # Time) through the "kubernetes.io/egress-bandwidth" Pod annotation. @@ -468,8 +505,7 @@ l2podAnnouncements: interface: "eth0" # -- A regular expression matching interfaces used for sending Gratuitous ARP pod announcements # interfacePattern: "" -# -- This feature set enables virtual BGP routers to be created via -# CiliumBGPPeeringPolicy CRDs. +# -- This feature set enables virtual BGP routers to be created via BGP CRDs. bgpControlPlane: # -- Enables the BGP control plane. enabled: false @@ -479,9 +515,9 @@ bgpControlPlane: create: false # -- The name of the secret namespace to which Cilium agents are given read access name: kube-system - # -- Status reporting settings (BGPv2 only) + # -- Status reporting settings statusReport: - # -- Enable/Disable BGPv2 status reporting + # -- Enable/Disable BGP status reporting # It is recommended to enable status reporting in general, but if you have any issue # such as high API server load, you can disable it by setting this to false. enabled: true @@ -491,7 +527,7 @@ bgpControlPlane: mode: "default" # -- IP pool to allocate the BGP router-id from when the mode is ip-pool. ipPool: "" - # -- Legacy BGP ORIGIN attribute settings (BGPv2 only) + # -- Legacy BGP ORIGIN attribute settings legacyOriginAttribute: # -- Enable/Disable advertising LoadBalancerIP routes with the legacy # BGP ORIGIN attribute value INCOMPLETE (2) instead of the default IGP (0). @@ -501,6 +537,11 @@ pmtuDiscovery: # -- Enable path MTU discovery to send ICMP fragmentation-needed replies to # the client. enabled: false + # -- Enable kernel probing path MTU discovery for Pods which uses different message + # sizes to search for correct MTU value. + # Valid values are: always, blackhole, disabled and unset (or empty). If value + # is 'unset' or left empty then will not try to override setting. + packetizationLayerPMTUDMode: "blackhole" bpf: autoMount: # -- Enable automatic mount of BPF filesystem @@ -548,7 +589,7 @@ bpf: # Helm configuration for BPF events map rate limiting is experimental and might change # in upcoming releases. events: - # -- Default settings for all types of events except dbg and pcap. + # -- Default settings for all types of events except dbg. default: # @schema # type: [null, integer] @@ -608,6 +649,12 @@ bpf: # type: [null, integer] # @schema policyMapMax: 16384 + # -- (float64) Configure threshold for emitting pressure metrics of policy maps. + # @schema + # type: [null, number] + # @schema + # @default -- `0.1` + policyMapPressureMetricsThreshold: ~ # -- Configure the maximum number of entries in global policy stats map. # @schema # type: [null, integer] @@ -665,7 +712,8 @@ bpf: # type: [null, boolean] # @schema # -- (bool) Configure the eBPF-based TPROXY (beta) to reduce reliance on iptables rules - # for implementing Layer 7 policy. + # for implementing Layer 7 policy. Note this is incompatible with netkit (`bpf.datapathMode=netkit`, + # `bpf.datapathMode=netkit-l2`). # @default -- `false` tproxy: ~ # @schema @@ -675,6 +723,15 @@ bpf: # [0] will allow all VLAN id's without any filtering. # @default -- `[]` vlanBypass: ~ + # -- Configure the IP tracing option type. + # This option is used to specify the IP option type to use for tracing. + # The value must be an integer between 0 and 255. + # @schema + # type: [null, integer] + # minimum: 0 + # maximum: 255 + # @schema + monitorTraceIPOption: 0 # -- (bool) Disable ExternalIP mitigation (CVE-2020-8554) # @default -- `false` disableExternalIPMitigation: false @@ -682,7 +739,8 @@ bpf: # supported kernels. # @default -- `true` enableTCX: true - # -- (string) Mode for Pod devices for the core datapath (veth, netkit, netkit-l2) + # -- (string) Mode for Pod devices for the core datapath (veth, netkit, netkit-l2). + # Note netkit is incompatible with TPROXY (`bpf.tproxy`). # @default -- `veth` datapathMode: veth # -- Enable BPF clock source probing for more efficient tick retrieval. @@ -770,8 +828,17 @@ cni: # -- Specifies the resources for the cni initContainer resources: requests: + # @schema + # type: [integer, string] + # @schema cpu: 100m memory: 10Mi + limits: + # @schema + # type: [integer, string] + # @schema + cpu: 1 + memory: 1Gi # -- Enable route MTU for pod netns when CNI chaining is used enableRouteMTUForCNIChaining: false # -- Enable the removal of iptables rules created by the AWS CNI VPC plugin. @@ -796,10 +863,6 @@ conntrackGCMaxInterval: "" # -- (string) Configure timeout in which Cilium will exit if CRDs are not available # @default -- `"5m"` crdWaitTimeout: "" -# -- Tail call hooks for custom eBPF programs. -customCalls: - # -- Enable tail call hooks for custom eBPF programs. - enabled: false daemon: # -- Configure where Cilium runtime state should be stored. runPath: "/var/run/cilium" @@ -842,6 +905,12 @@ daemon: # # By default, this functionality is enabled enableSourceIPVerification: true +# -- Configure temporary volume for cilium-agent +tmpVolume: {} +# emptyDir: +# sizeLimit: "100Mi" +# medium: "Memory" + # -- Specify which network interfaces can run the eBPF datapath. This means # that a packet sent from a pod to a destination outside the cluster will be # masqueraded (to an output device IPv4 address), if the output device runs the @@ -860,9 +929,6 @@ forceDeviceDetection: false # -- Enable setting identity mark for local traffic. # enableIdentityMark: true -# -- Enable Kubernetes EndpointSlice feature in Cilium if the cluster supports it. -# enableK8sEndpointSlice: true - # -- CiliumEndpointSlice configuration options. ciliumEndpointSlice: # -- Enable Cilium EndpointSlice feature. @@ -1042,20 +1108,33 @@ enableXTSocketFallback: true encryption: # -- Enable transparent network encryption. enabled: false - # -- Encryption method. Can be either ipsec or wireguard. + # -- Encryption method. Can be one of ipsec, wireguard or ztunnel. type: ipsec # -- Enable encryption for pure node to node traffic. # This option is only effective when encryption.type is set to "wireguard". nodeEncryption: false - # -- Configure the WireGuard Pod2Pod strict mode. + # -- Configure the Encryption Pod2Pod strict mode. strictMode: - # -- Enable WireGuard Pod2Pod strict mode. + # -- Enable Encryption Pod2Pod strict mode. (deprecated: please use encryption.strictMode.egress.enabled) enabled: false - # -- CIDR for the WireGuard Pod2Pod strict mode. + # -- CIDR for the Encryption Pod2Pod strict mode. (deprecated: please use encryption.strictMode.egress.cidr) cidr: "" - # -- Allow dynamic lookup of remote node identities. + # -- Allow dynamic lookup of remote node identities. (deprecated: please use encryption.strictMode.egress.allowRemoteNodeIdentities) # This is required when tunneling is used or direct routing is used and the node CIDR and pod CIDR overlap. allowRemoteNodeIdentities: false + egress: + # -- Enable strict egress encryption. + enabled: false + # -- CIDR for the Encryption Pod2Pod strict egress mode. + cidr: "" + # -- Allow dynamic lookup of remote node identities. + # This is required when tunneling is used or direct routing is used and the node CIDR and pod CIDR overlap. + allowRemoteNodeIdentities: false + ingress: + # -- Enable strict ingress encryption. + # When enabled, all unencrypted overlay ingress traffic will be dropped. + # This option is only applicable when WireGuard and tunneling are enabled. + enabled: false ipsec: # -- Name of the key file inside the Kubernetes secret configured via secretName. keyFile: keys @@ -1076,9 +1155,89 @@ encryption: wireguard: # -- Controls WireGuard PersistentKeepalive option. Set 0s to disable. persistentKeepalive: 0s + # -- ztunnel encryption configuration. + # ztunnel is Istio's purpose-built, per-node proxy for handling L4 traffic in ambient mesh mode. + # These settings only apply when encryption.type is set to "ztunnel". + ztunnel: + # -- ztunnel container image. + image: + # @schema + # type: [null, string] + # @schema + override: ~ + repository: "docker.io/istio/ztunnel" + tag: "1.28.0-distroless" + pullPolicy: "IfNotPresent" + # @schema + # type: [null, string] + # @schema + digest: ~ + useDigest: false + # -- CA server address for certificate requests. + caAddress: "https://localhost:15012" + # -- TCP port for the health API. + healthPort: 15021 + # -- ztunnel resource limits & requests. + resources: + requests: + cpu: 200m + memory: 512Mi + # -- ztunnel update strategy. + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 + # -- Configure termination grace period for ztunnel DaemonSet. + terminationGracePeriodSeconds: 30 + # -- Readiness probe configuration. + readinessProbe: + initialDelaySeconds: 0 + periodSeconds: 10 + failureThreshold: 3 + # -- Node selector for ztunnel pods. + nodeSelector: + kubernetes.io/os: linux + # -- Node tolerations for ztunnel scheduling. + tolerations: + - effect: NoSchedule + operator: Exists + - key: CriticalAddonsOnly + operator: Exists + - effect: NoExecute + operator: Exists + # -- Affinity for ztunnel pods. + affinity: {} + # @schema + # type: [null, string] + # @schema + # -- The priority class to use for ztunnel pods. + priorityClassName: ~ + # -- Annotations to be added to all ztunnel resources. + annotations: {} + # -- Annotations to be added to ztunnel pods. + podAnnotations: {} + # -- Labels to be added to ztunnel pods. + podLabels: {} + # -- Additional ztunnel container environment variables. + extraEnv: [] + # -- Additional ztunnel volumes. + extraVolumes: [] + # -- Additional ztunnel volumeMounts. + extraVolumeMounts: [] + # -- ztunnel secrets configuration. + secrets: + # -- Base64-encoded bootstrap root certificate content. + # If not provided, the secret must be created manually before deploying. + # @schema + # type: [null, string] + # @schema + bootstrapRootCert: ~ endpointHealthChecking: # -- Enable connectivity health checking between virtual endpoints. enabled: true +# -- Max duration to wait for envoy to respond to configuration changes. Default "10s". +endpointPolicyUpdateTimeoutDuration: null endpointRoutes: # @schema # type: [boolean, string] @@ -1127,6 +1286,32 @@ eni: # -- Filter via AWS EC2 Instance tags (k=v) which will dictate which AWS EC2 Instances # are going to be used to create new ENIs instanceTagsFilter: [] + # -- NodeSpec configuration for the ENI + nodeSpec: + # -- First interface index to use for IP allocation + # @schema + # type: [null, integer] + # @schema + firstInterfaceIndex: ~ + # -- Subnet IDs to use for IP allocation + subnetIDs: [] + # -- Subnet tags to use for IP allocation + subnetTags: [] + # -- Security groups to use for IP allocation + securityGroups: [] + # -- Security group tags to use for IP allocation + securityGroupTags: [] + # -- Exclude interface tags to use for IP allocation + excludeInterfaceTags: [] + # -- Use primary address for IP allocation + usePrimaryAddress: false + # -- Disable prefix delegation for IP allocation + disablePrefixDelegation: false + # -- Delete ENI on termination + # @schema + # type: [null, boolean] + # @schema + deleteOnTermination: ~ # fragmentTracking enables IPv4 fragment tracking support in the datapath. # fragmentTracking: true gke: @@ -1142,6 +1327,8 @@ healthCheckICMPFailureThreshold: 3 hostFirewall: # -- Enables the enforcement of host policies in the eBPF datapath. enabled: false +# -- Enable routing to a service that has zero endpoints +enableNoServiceEndpointsRoutable: true # -- Configure socket LB socketLB: # -- Enable socket LB @@ -1165,12 +1352,15 @@ certgen: # @schema override: ~ repository: "quay.io/cilium/certgen" - tag: "v0.3.1" - digest: "sha256:2825dbfa6f89cbed882fd1d81e46a56c087e35885825139923aa29eb8aec47a9" + tag: "v0.4.1" + digest: "sha256:f0c656830e856d26b24b0e144df1f8b327d3b46748d76a630514111fc365b697" useDigest: true pullPolicy: "IfNotPresent" + # @schema + # type: [null, integer] + # @schema # -- Seconds after which the completed job pod will be deleted - ttlSecondsAfterFinished: 1800 + ttlSecondsAfterFinished: null # -- Labels to be added to hubble-certgen pods podLabels: {} # -- Annotations to be added to the hubble-certgen initial Job and CronJob @@ -1195,6 +1385,11 @@ certgen: extraVolumeMounts: [] # -- Affinity for certgen affinity: {} + cronJob: + # -- The number of successful finished jobs to keep + successfulJobsHistoryLimit: 3 + # -- The number of failed finished jobs to keep + failedJobsHistoryLimit: 1 hubble: # -- Enable Hubble (true by default). enabled: true @@ -1210,6 +1405,9 @@ hubble: # 2047, 4095, 8191, 16383, 32767, 65535 # eventBufferCapacity: "4095" + # -- The interval at which Hubble will send out lost events from the Observer server, if any. + # lostEventSendInterval: 1s + # -- Hubble metrics configuration. # See https://docs.cilium.io/en/stable/observability/metrics/#hubble-metrics # for more comprehensive documentation about Hubble metrics. @@ -1503,9 +1701,9 @@ hubble: # @schema override: ~ repository: "quay.io/cilium/hubble-relay" - tag: "v1.18.6" + tag: "v1.19.3" # hubble-relay-digest - digest: sha256:fb6135e34c31e5f175cb5e75f86cea52ef2ff12b49bcefb7088ed93f5009eb8e + digest: sha256:5ee21d57b6ef2aa6db67e603a735fdceb162454b352b7335b651456e308f681b useDigest: true pullPolicy: "IfNotPresent" # -- Specifies the resources for the hubble-relay pods @@ -1716,6 +1914,24 @@ hubble: address: localhost # -- Configure pprof listen port for hubble-relay port: 6062 + # -- Enable mutex contention profiling for hubble-relay and set the fraction of sampled events (set to 1 to sample all events) + mutexProfileFraction: 0 + # -- Enable goroutine blocking profiling for hubble-relay and set the rate of sampled events in nanoseconds (set to 1 to sample all events [warning: performance overhead]) + blockProfileRate: 0 + # -- Logging configuration for hubble-relay. + logOptions: + # @schema + # type: [null, string] + # @schema + # -- Log format for hubble-relay. Valid values are: text, text-ts, json, json-ts. + # @default -- text-ts + format: ~ + # @schema + # type: [null, string] + # @schema + # -- Log level for hubble-relay. Valid values are: debug, info, warn, error. + # @default -- info + level: ~ ui: # -- Whether to enable the Hubble UI. enabled: false @@ -1911,6 +2127,11 @@ hubble: # - secretName: chart-example-tls # hosts: # - chart-example.local + # -- Configure temporary volume for hubble-ui + tmpVolume: {} + # emptyDir: + # # sizeLimit: "100Mi" + # # medium: "Memory" # -- Hubble flows export. export: # --- Static exporter configuration. @@ -1923,6 +2144,14 @@ hubble: # - source # - destination # - verdict + fieldAggregate: [] + # - time + # - source + # - destination + # - verdict + # --- Defines the interval at which to aggregate before exporting Hubble flows. + # Aggregation feature is only enabled when fieldAggregate is specified and aggregationInterval > 0s. + aggregationInterval: "0s" allowList: [] # - '{"verdict":["DROPPED","ERROR"]}' denyList: [] @@ -1948,6 +2177,8 @@ hubble: content: - name: all fieldMask: [] + fieldAggregate: [] + aggregationInterval: "0s" includeFilters: [] excludeFilters: [] filePath: "/var/run/cilium/hubble/events.log" @@ -2040,11 +2271,30 @@ ipam: # refill the bucket up to the burst size capacity. # @default -- `4.0` externalAPILimitQPS: ~ -# -- defaultLBServiceIPAM indicates the default LoadBalancer Service IPAM when -# no LoadBalancer class is set. Applicable values: lbipam, nodeipam, none + # -- NodeSpec configuration for the IPAM + nodeSpec: + # -- IPAM min allocate + # @schema + # type: [null, integer] + # @schema + ipamMinAllocate: ~ + # -- IPAM pre allocate + # @schema + # type: [null, integer] + # @schema + ipamPreAllocate: ~ + # -- IPAM max allocate + # @schema + # type: [null, integer] + # @schema + ipamMaxAllocate: ~ + # -- IPAM static IP tags (currently only works with AWS and Azure) + ipamStaticIPTags: [] # @schema # type: [string] # @schema +# -- defaultLBServiceIPAM indicates the default LoadBalancer Service IPAM when +# no LoadBalancer class is set. Applicable values: lbipam, nodeipam, none defaultLBServiceIPAM: lbipam nodeIPAM: # -- Configure Node IPAM @@ -2155,7 +2405,7 @@ maglev: {} # type: [null, boolean] # @schema # -- (bool) Enables masquerading of IPv4 traffic leaving the node from endpoints. -# @default -- `true` unless ipam eni mode is active +# @default -- `true` unless ipam eni mode is active enableIPv4Masquerade: ~ # -- Enables masquerading of IPv6 traffic leaving the node from endpoints. enableIPv6Masquerade: true @@ -2242,8 +2492,7 @@ loadBalancer: # -- serviceTopology enables K8s Topology Aware Hints -based service # endpoints filtering - # serviceTopology: false - + serviceTopology: false # -- L7 LoadBalancer l7: # -- Enable L7 service load balancing via envoy proxy. @@ -2266,8 +2515,6 @@ loadBalancer: algorithm: round_robin # -- Configure N-S k8s service loadbalancing nodePort: - # -- Enable the Cilium NodePort service implementation. - enabled: false # -- Port range to use for NodePort services. # range: "30000,32767" @@ -2311,6 +2558,10 @@ pprof: address: localhost # -- Configure pprof listen port for cilium-agent port: 6060 + # -- Enable mutex contention profiling for cilium-agent and set the fraction of sampled events (set to 1 to sample all events) + mutexProfileFraction: 0 + # -- Enable goroutine blocking profiling for cilium-agent and set the rate of sampled events in nanoseconds (set to 1 to sample all events [warning: performance overhead]) + blockProfileRate: 0 # -- Configure prometheus metrics on the configured port at /metrics prometheus: metricsService: false @@ -2435,6 +2686,12 @@ envoy: initialFetchTimeoutSeconds: 30 # -- Maximum number of concurrent retries on Envoy clusters maxConcurrentRetries: 128 + # -- Maximum number of connections on Envoy clusters + clusterMaxConnections: 1024 + # -- Maximum number of requests on Envoy clusters + clusterMaxRequests: 1024 + # -- Maximum number of global downstream connections + maxGlobalDownstreamConnections: 50000 # -- Maximum number of retries for each HTTP request httpRetryCount: 3 # -- ProxyMaxRequestsPerConnection specifies the max_requests_per_connection setting for Envoy @@ -2451,6 +2708,9 @@ envoy: xffNumTrustedHopsL7PolicyIngress: 0 # -- Number of trusted hops regarding the x-forwarded-for and related HTTP headers for the egress L7 policy enforcement Envoy listeners. xffNumTrustedHopsL7PolicyEgress: 0 + # -- For cases when CiliumEnvoyConfig is not used directly (Ingress, Gateway), configures Cilium BPF Metadata listener filter + # to use the original source address when extracting the metadata for a request. + useOriginalSourceAddress: true # @schema # type: [null, string] # @schema @@ -2465,10 +2725,12 @@ envoy: # @schema override: ~ repository: "quay.io/cilium/cilium-envoy" - tag: "v1.35.9-1767794330-db497dd19e346b39d81d7b5c0dedf6c812bcc5c9" + tag: "v1.36.6-1776000132-2437d2edeaf4d9b56ef279bd0d71127440c067aa" pullPolicy: "IfNotPresent" - digest: "sha256:81398e449f2d3d0a6a70527e4f641aaa685d3156bea0bb30712fae3fd8822b86" + digest: "sha256:ba0ab8adac082d50d525fd2c5ba096c8facea3a471561b7c61c7a5b9c2e0de0d" useDigest: true + # -- Init containers added to the cilium Envoy DaemonSet. + initContainers: [] # -- Additional containers added to the cilium Envoy DaemonSet. extraContainers: [] # -- Additional envoy container arguments. @@ -2699,16 +2961,15 @@ resourceQuotas: pods: "15" # Need to document default ################## -#sessionAffinity: false # -- Annotations to be added to all cilium-secret namespaces (resources under templates/cilium-secrets-namespace) secretsNamespaceAnnotations: {} +# -- Labels to be added to all cilium-secret namespaces (resources under templates/cilium-secrets-namespace) +secretsNamespaceLabels: {} # -- Do not run Cilium agent when running with clean mode. Useful to completely # uninstall Cilium as it will stop Cilium from starting and create artifacts # in the node. sleepAfterInit: false -# -- Enable check of service source ranges (currently, only for LoadBalancer). -svcSourceRangeCheck: true # -- Synchronize Kubernetes nodes to kvstore and perform CNP GC. synchronizeK8sNodes: true # -- Configure TLS configuration in the agent. @@ -2791,6 +3052,9 @@ tls: # @default -- `"vxlan"` tunnelProtocol: "" # -- IP family for the underlay. +# Possible values: +# - "ipv4" +# - "ipv6" # @default -- `"ipv4"` underlayProtocol: "" # -- Enable native-routing mode or tunneling mode. @@ -2811,6 +3075,11 @@ tunnelSourcePortRange: 0-0 # - reject (default) # - drop serviceNoBackendResponse: reject +# -- Configure what the response should be to pod egress traffic denied by network policy. +# Possible values: +# - none (default) +# - icmp +policyDenyResponse: none # -- Configure the underlying network MTU to overwrite auto-detected MTU. # This value doesn't change the host network interface MTU i.e. eth0 or ens0. # It changes the MTU for cilium_net@cilium_host, cilium_host@cilium_net, @@ -2841,15 +3110,15 @@ operator: # @schema override: ~ repository: "quay.io/cilium/operator" - tag: "v1.18.6" + tag: "v1.19.3" # operator-generic-digest - genericDigest: sha256:34a827ce9ed021c8adf8f0feca131f53b3c54a3ef529053d871d0347ec4d69af + genericDigest: sha256:205b09b0ed6accbf9fe688d312a9f0fcfc6a316fc081c23fbffb472af5dd62cd # operator-azure-digest - azureDigest: sha256:a57aff47aeb32eccfedaa2a49d1af984d996d6d6de79609c232e0c4cf9ce97a1 + azureDigest: sha256:699c1571a3df1a98882ee13610d47cffb7b34ee7e8d276096db798a5f6c7e4cb # operator-aws-digest - awsDigest: sha256:47dbc1a5bd483fec170dab7fb0bf2cca3585a4893675b0324d41d97bac8be5eb + awsDigest: sha256:a53dcbfb77282bf2ddd3abbe60f6d49762e7c1389a36cb35b71d504644a56640 # operator-alibabacloud-digest - alibabacloudDigest: sha256:212c4cbe27da3772bcb952b8f8cbaa0b0eef72488b52edf90ad2b32072a3ca4c + alibabacloudDigest: sha256:176321a65123373ff8c7823b25183102cbad98375e8d6c80b96d68b6e8491103 useDigest: true pullPolicy: "IfNotPresent" suffix: "" @@ -2988,6 +3257,10 @@ operator: address: localhost # -- Configure pprof listen port for cilium-operator port: 6061 + # -- Enable mutex contention profiling for cilium-operator and set the fraction of sampled events (set to 1 to sample all events) + mutexProfileFraction: 0 + # -- Enable goroutine blocking profiling for cilium-operator and set the rate of sampled events in nanoseconds (set to 1 to sample all events [warning: performance overhead]) + blockProfileRate: 0 # -- Enable prometheus metrics for cilium-operator on the configured port at # /metrics prometheus: @@ -3021,6 +3294,17 @@ operator: # @schema # -- Metrics relabeling configs for the ServiceMonitor cilium-operator metricRelabelings: ~ + # -- TLS configuration for Prometheus + tls: + enabled: false + server: + # -- Name of the Secret containing the certificate, key and CA files for the Prometheus server. + existingSecret: "" + mtls: + # When set to true enforces mutual TLS between Operator Prometheus server and its clients. + # False allow non-mutual TLS connections. + # This option has no effect when TLS is disabled. + enabled: false # -- Grafana dashboards for cilium-operator # grafana can import dashboards based on the label and value # ref: https://github.com/grafana/helm-charts/tree/main/charts/grafana#sidecar-for-dashboards @@ -3054,6 +3338,12 @@ operator: # -- Interval, in seconds, to check if there are any pods that are not # managed by Cilium. intervalSeconds: 15 + # -- Selector for pods that should be restarted when not managed by Cilium. + # If not set, defaults to built-in selector "k8s-app=kube-dns". Set to empty string to select all pods. + # @schema + # type: [null, string] + # @schema + selector: ~ nodeinit: # -- Enable the node initialization DaemonSet enabled: false @@ -3064,8 +3354,8 @@ nodeinit: # @schema override: ~ repository: "quay.io/cilium/startup-script" - tag: "1755531540-60ee83e" - digest: "sha256:5bdca3c2dec2c79f58d45a7a560bf1098c2126350c901379fe850b7f78d3d757" + tag: "1763560095-8f36c34" + digest: "sha256:50b9cf9c280096b59b80d2fc8ee6638facef79ac18998a22f0cbc40d5d28c16f" useDigest: true pullPolicy: "IfNotPresent" # -- The priority class to use for the nodeinit pod. @@ -3108,6 +3398,9 @@ nodeinit: # ref: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ resources: requests: + # @schema + # type: [integer, string] + # @schema cpu: 100m memory: 100Mi # -- Security context to be added to nodeinit pods. @@ -3130,6 +3423,8 @@ nodeinit: # -- bootstrapFile is the location of the file where the bootstrap timestamp is # written by the node-init DaemonSet bootstrapFile: "/tmp/cilium-bootstrap.d/cilium-bootstrap-time" + # -- wait for Cloud init to finish on the host and assume the node has cloud init installed + waitForCloudInit: false # -- startup offers way to customize startup nodeinit script (pre and post position) startup: preScript: "" @@ -3148,9 +3443,9 @@ preflight: # @schema override: ~ repository: "quay.io/cilium/cilium" - tag: "v1.18.6" + tag: "v1.19.3" # cilium-digest - digest: sha256:42ec562a5ff6c8a860c0639f5a7611685e253fd9eb2d2fcdade693724c9166a4 + digest: sha256:2e61680593cddca8b6c055f6d4c849d87a26a1c91c7e3b8b56c7fb76ab7b7b10 useDigest: true pullPolicy: "IfNotPresent" envoy: @@ -3161,9 +3456,9 @@ preflight: # @schema override: ~ repository: "quay.io/cilium/cilium-envoy" - tag: "v1.35.9-1767794330-db497dd19e346b39d81d7b5c0dedf6c812bcc5c9" + tag: "v1.36.6-1776000132-2437d2edeaf4d9b56ef279bd0d71127440c067aa" pullPolicy: "IfNotPresent" - digest: "sha256:81398e449f2d3d0a6a70527e4f641aaa685d3156bea0bb30712fae3fd8822b86" + digest: "sha256:ba0ab8adac082d50d525fd2c5ba096c8facea3a471561b7c61c7a5b9c2e0de0d" useDigest: true # -- The priority class to use for the preflight pod. priorityClassName: "" @@ -3263,7 +3558,9 @@ enableCriticalPriorityClass: true # on AArch64 as the images do not currently ship a version of Envoy. #disableEnvoyVersionCheck: false clustermesh: - # -- Deploy clustermesh-apiserver for clustermesh + # -- Deploy clustermesh-apiserver for clustermesh. This option is typically + # used with ``clustermesh.config.enabled=true``. Refer to the + # ``clustermesh.config.enabled=true``documentation for more information. useAPIServer: false # -- The maximum number of clusters to support in a ClusterMesh. This value # cannot be changed on running clusters, and all clusters in a ClusterMesh @@ -3271,44 +3568,132 @@ clustermesh: # maximum allocatable cluster-local identities. # Supported values are 255 and 511. maxConnectedClusters: 255 + # -- The time to live for the cache of a remote cluster after connectivity is + # lost. If the connection is not re-established within this duration, the + # cached data is revoked to prevent stale state. If not specified or set to + # 0s, the cache is never revoked (default). + cacheTTL: "0s" # -- Enable the synchronization of Kubernetes EndpointSlices corresponding to # the remote endpoints of appropriately-annotated global services through ClusterMesh enableEndpointSliceSynchronization: false - # -- Enable Multi-Cluster Services API support + # -- Enable Multi-Cluster Services API support (deprecated; use clustermesh.mcsapi.enabled) enableMCSAPISupport: false # -- Control whether policy rules assume by default the local cluster if not explicitly selected - policyDefaultLocalCluster: false + policyDefaultLocalCluster: true # -- Annotations to be added to all top-level clustermesh objects (resources under templates/clustermesh-apiserver and templates/clustermesh-config) annotations: {} # -- Clustermesh explicit configuration. config: # -- Enable the Clustermesh explicit configuration. + # If set to false, you need to provide the following resources yourself: + # - (Secret) cilium-clustermesh (used by cilium-agent/cilium-operator to connect to + # the local etcd instance if KVStoreMesh is enabled or the remote clusters + # if KVStoreMesh is disabled) + # - (Secret) cilium-kvstoremesh (used by KVStoreMesh to connect to the remote clusters) + # - (ConfigMap) clustermesh-remote-users (used to create one etcd user per remote cluster + # if clustermesh-apiserver is used and `clustermesh.apiserver.tls.authMode` is not + # set to `legacy`) enabled: false # -- Default dns domain for the Clustermesh API servers # This is used in the case cluster addresses are not provided # and IPs are used. domain: mesh.cilium.io - # -- List of clusters to be peered in the mesh. + # -- Clusters to be peered in the mesh. + # @schema + # type: [object, array] + # @schema clusters: [] + # You can use a dict of clusters (recommended): # clusters: - # # -- Name of the cluster + # # -- Name of the cluster + # cluster1: + # # -- Whether to enable this cluster in the mesh. Optional, defaults to true. + # enabled: true + # # -- Address of the cluster, use this if you created DNS records for + # # the cluster Clustermesh API server. + # address: cluster1.mesh.cilium.io + # # -- Port of the cluster Clustermesh API server. + # port: 2379 + # # -- IPs of the cluster Clustermesh API server, use multiple ones when + # # you have multiple IPs to access the Clustermesh API server. + # ips: + # - 172.18.255.201 + # # -- (deprecated) base64 encoded PEM values for the cluster client certificate, private key and certificate authority. + # # These fields can (and should) be omitted in case the CA is shared across clusters. In that case, the + # # "remote" private key and certificate available in the local cluster are automatically used instead. + # tls: + # cert: "" + # key: "" + # caCert: "" + # + # Or alternatively you can use a list of clusters: + # clusters: + # # -- Name of the cluster # - name: cluster1 - # # -- Address of the cluster, use this if you created DNS records for - # # the cluster Clustermesh API server. + # # -- Address of the cluster, use this if you created DNS records for + # # the cluster Clustermesh API server. # address: cluster1.mesh.cilium.io - # # -- Port of the cluster Clustermesh API server. + # # -- Port of the cluster Clustermesh API server. # port: 2379 - # # -- IPs of the cluster Clustermesh API server, use multiple ones when - # # you have multiple IPs to access the Clustermesh API server. + # # -- IPs of the cluster Clustermesh API server, use multiple ones when + # # you have multiple IPs to access the Clustermesh API server. # ips: # - 172.18.255.201 - # # -- base64 encoded PEM values for the cluster client certificate, private key and certificate authority. - # # These fields can (and should) be omitted in case the CA is shared across clusters. In that case, the - # # "remote" private key and certificate available in the local cluster are automatically used instead. + # # -- (deprecated) base64 encoded PEM values for the cluster client certificate, private key and certificate authority. + # # These fields can (and should) be omitted in case the CA is shared across clusters. In that case, the + # # "remote" private key and certificate available in the local cluster are automatically used instead. # tls: # cert: "" # key: "" # caCert: "" + mcsapi: + # -- Enable Multi-Cluster Services API support + enabled: false + # -- Enabled MCS-API CRDs auto-installation + installCRDs: true + corednsAutoConfigure: + # -- Enable auto-configuration of CoreDNS for Multi-Cluster Services API. + # CoreDNS MUST be at least in version v1.12.2 to run this. + enabled: false + coredns: + # -- The Deployment for the cluster CoreDNS service + deploymentName: coredns + # -- The Service Account name for the cluster CoreDNS service + serviceAccountName: coredns + # -- The ConfigMap name for the cluster CoreDNS service + configMapName: coredns + # -- The namespace for the cluster CoreDNS service + namespace: kube-system + # -- The cluster domain for the cluster CoreDNS service + clusterDomain: cluster.local + # -- The clusterset domain for the cluster CoreDNS service + clustersetDomain: clusterset.local + # -- Additional arguments to `clustermesh-apiserver coredns-mcsapi-auto-configure`. + extraArgs: [] + # -- Seconds after which the completed job pod will be deleted + ttlSecondsAfterFinished: 1800 + # -- Labels to be added to coredns-mcsapi-autoconfig pods + podLabels: {} + # -- Annotations to be added to the coredns-mcsapi-autoconfig Job + annotations: {} + # -- Node selector for coredns-mcsapi-autoconfig + # ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector + nodeSelector: {} + # -- Priority class for coredns-mcsapi-autoconfig + # ref: https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/#priorityclass + priorityClassName: "" + # -- Node tolerations for pod assignment on nodes with taints + # ref: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ + tolerations: [] + # -- Resource limits for coredns-mcsapi-autoconfig + # ref: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers + resources: {} + # -- Additional coredns-mcsapi-autoconfig volumes. + extraVolumes: [] + # -- Additional coredns-mcsapi-autoconfig volumeMounts. + extraVolumeMounts: [] + # -- Affinity for coredns-mcsapi-autoconfig + affinity: {} apiserver: # -- Clustermesh API server image. image: @@ -3317,9 +3702,9 @@ clustermesh: # @schema override: ~ repository: "quay.io/cilium/clustermesh-apiserver" - tag: "v1.18.6" + tag: "v1.19.3" # clustermesh-apiserver-digest - digest: sha256:8ee142912a0e261850c0802d9256ddbe3729e1cd35c6bea2d93077f334c3cf3b + digest: sha256:a8136a7615d6c6041d3aa6f2674d17beaec238170d669507ccc05328a778e2b7 useDigest: true pullPolicy: "IfNotPresent" # -- TCP port for the clustermesh-apiserver health API. @@ -3408,17 +3793,12 @@ clustermesh: # - "external": ``clustermesh-apiserver`` will sync remote cluster information to the etcd used as kvstore. This can't be enabled with crd identity allocation mode. kvstoreMode: "internal" service: + # -- (bool) Set externallyCreated to true to create the clustermesh-apiserver service outside this helm chart. + # For example after external load balancer controllers are created. + externallyCreated: false # -- The type of service used for apiserver access. type: NodePort # -- Optional port to use as the node port for apiserver access. - # - # WARNING: make sure to configure a different NodePort in each cluster if - # kube-proxy replacement is enabled, as Cilium is currently affected by a known - # bug (#24692) when NodePorts are handled by the KPR implementation. If a service - # with the same NodePort exists both in the local and the remote cluster, all - # traffic originating from inside the cluster and targeting the corresponding - # NodePort will be redirected to a local backend, regardless of whether the - # destination node belongs to the local or the remote cluster. nodePort: 32379 # -- Annotations for the clustermesh-apiserver service. # Example annotations to configure an internal load balancer on different cloud providers: @@ -3587,13 +3967,15 @@ clustermesh: # The "remote" certificate must be generated with CN=remote- # if provided manually. Cluster mode is meaningful only when the same # CA is shared across all clusters part of the mesh. - authMode: legacy - # -- Allow users to provide their own certificates + authMode: migration + # -- (deprecated) Allow users to provide their own certificates # Users may need to provide their certificates using # a mechanism that requires they provide their own secrets. # This setting does not apply to any of the auto-generated # mechanisms below, it only restricts the creation of secrets # via the `tls-provided` templates. + # This option is deprecated as secrets are expected to be created + # externally when 'auto' is not enabled. enableSecrets: true # -- Configure automatic TLS certificates generation. # A Kubernetes CronJob is used the generate any @@ -3602,7 +3984,14 @@ clustermesh: auto: # -- When set to true, automatically generate a CA and certificates to # enable mTLS between clustermesh-apiserver and external workload instances. - # If set to false, the certs to be provided by setting appropriate values below. + # + # When set to false you need to pre-create the following secrets: + # - clustermesh-apiserver-server-cert + # - clustermesh-apiserver-admin-cert + # - clustermesh-apiserver-remote-cert + # - clustermesh-apiserver-local-cert + # The above secret should at least contains the keys `tls.crt` and `tls.key` + # and optionally `ca.crt` if a CA bundle is not configured. enabled: true # Sets the method to auto-generate certificates. Supported values: # - helm: This method uses Helm to generate all certificates. @@ -3637,7 +4026,9 @@ clustermesh: # -- base64 encoded PEM values for the clustermesh-apiserver server certificate and private key. # Used if 'auto' is not enabled. server: + # -- Deprecated, as secrets will always need to be created externally if `auto` is disabled. cert: "" + # -- Deprecated, as secrets will always need to be created externally if `auto` is disabled. key: "" # -- Extra DNS names added to certificate when it's auto generated extraDnsNames: [] @@ -3646,17 +4037,16 @@ clustermesh: # -- base64 encoded PEM values for the clustermesh-apiserver admin certificate and private key. # Used if 'auto' is not enabled. admin: + # -- Deprecated, as secrets will always need to be created externally if `auto` is disabled. cert: "" - key: "" - # -- base64 encoded PEM values for the clustermesh-apiserver client certificate and private key. - # Used if 'auto' is not enabled. - client: - cert: "" + # -- Deprecated, as secrets will always need to be created externally if `auto` is disabled. key: "" # -- base64 encoded PEM values for the clustermesh-apiserver remote cluster certificate and private key. # Used if 'auto' is not enabled. remote: + # -- Deprecated, as secrets will always need to be created externally if `auto` is disabled. cert: "" + # -- Deprecated, as secrets will always need to be created externally if `auto` is disabled. key: "" # clustermesh-apiserver Prometheus metrics configuration metrics: @@ -3811,7 +4201,7 @@ authentication: # -- Enable authentication processing and garbage collection. # Note that if disabled, policy enforcement will still block requests that require authentication. # But the resulting authentication requests for these requests will not be processed, therefore the requests not be allowed. - enabled: true + enabled: false # -- Buffer size of the channel Cilium uses to receive authentication events from the signal map. queueSize: 1024 # -- Buffer size of the channel Cilium uses to receive certificate expiration events from auth handlers. @@ -3849,7 +4239,7 @@ authentication: override: ~ repository: "docker.io/library/busybox" tag: "1.37.0" - digest: "sha256:2383baad1860bbe9d8a7a843775048fd07d8afe292b94bd876df64a69aae7cb1" + digest: "sha256:1487d0af5f52b4ba31c7e465126ee2123fe3f2305d638e7827681e7cf6c83d5e" useDigest: true pullPolicy: "IfNotPresent" # SPIRE agent configuration @@ -3863,8 +4253,8 @@ authentication: # @schema override: ~ repository: "ghcr.io/spiffe/spire-agent" - tag: "1.12.4" - digest: "sha256:163970884fba18860cac93655dc32b6af85a5dcf2ebb7e3e119a10888eff8fcd" + tag: "1.9.6" + digest: "sha256:5106ac601272a88684db14daf7f54b9a45f31f77bb16a906bd5e87756ee7b97c" useDigest: true pullPolicy: "IfNotPresent" # -- SPIRE agent service account @@ -3918,8 +4308,8 @@ authentication: # @schema override: ~ repository: "ghcr.io/spiffe/spire-server" - tag: "1.12.4" - digest: "sha256:34147f27066ab2be5cc10ca1d4bfd361144196467155d46c45f3519f41596e49" + tag: "1.9.6" + digest: "sha256:59a0b92b39773515e25e68a46c40d3b931b9c1860bc445a79ceb45a805cab8b4" useDigest: true pullPolicy: "IfNotPresent" # -- SPIRE server service account @@ -4004,3 +4394,41 @@ authentication: enableInternalTrafficPolicy: true # -- Enable LoadBalancer IP Address Management enableLBIPAM: true +# -- Standalone DNS Proxy Configuration +# Note: The standalone DNS proxy uses the agent's dnsProxy.* configuration +# for DNS settings (proxyPort, enableDnsCompression) to ensure consistency. +standaloneDnsProxy: + # -- Enable standalone DNS proxy (alpha feature) + enabled: false + # -- Roll out Standalone DNS proxy automatically when configmap is updated. + rollOutPods: false + # -- Standalone DNS proxy annotations + annotations: {} + # -- Standalone DNS proxy debug mode + debug: false + # -- Standalone DNS proxy server port + serverPort: 10095 + # -- Standalone DNS proxy Node Selector + nodeSelector: + kubernetes.io/os: linux + # -- Standalone DNS proxy tolerations + tolerations: [] + # -- Standalone DNS proxy auto mount service account token + automountServiceAccountToken: false + # -- Standalone DNS proxy update strategy + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 2 + maxUnavailable: 0 + # -- Standalone DNS proxy image + image: + # @schema + # type: [null, string] + # @schema + override: ~ + repository: "" + tag: "" + digest: "" + useDigest: true + pullPolicy: "IfNotPresent" diff --git a/packages/system/cilium/charts/cilium/values.yaml.tmpl b/packages/system/cilium/charts/cilium/values.yaml.tmpl index 83b87cfd..8039b40c 100644 --- a/packages/system/cilium/charts/cilium/values.yaml.tmpl +++ b/packages/system/cilium/charts/cilium/values.yaml.tmpl @@ -3,7 +3,6 @@ # type: [null, string] # @schema # -- namespaceOverride allows to override the destination namespace for Cilium resources. -# This property allows to use Cilium as part of an Umbrella Chart with different targets. namespaceOverride: "" # @schema # type: [null, object] @@ -27,7 +26,7 @@ debug: # @schema # -- Configure verbosity levels for debug logging # This option is used to enable debug messages for operations related to such - # sub-system such as (e.g. kvstore, envoy, datapath or policy), and flow is + # sub-system such as (e.g. kvstore, envoy, datapath, policy, or tagged), and flow is # for enabling debug messages emitted per request, message and connection. # Multiple values can be set via a space-separated string (e.g. "datapath envoy"). # @@ -37,6 +36,7 @@ debug: # - envoy # - datapath # - policy + # - tagged verbose: ~ # -- Set the agent-internal metrics sampling frequency. This sets the @@ -207,6 +207,18 @@ serviceAccounts: name: hubble-generate-certs automount: true annotations: {} + # -- CorednsMCSAPI is used if clustermesh.mcsapi.corednsAutoConfigure.enabled=true + corednsMCSAPI: + create: true + name: cilium-coredns-mcsapi-autoconfig + automount: true + annotations: {} + # -- Ztunnel is used if encryption.type=ztunnel + ztunnel: + create: true + name: ztunnel-cilium + automount: false + annotations: {} # -- Configure termination grace period for cilium-agent DaemonSet. terminationGracePeriodSeconds: 1 # -- Install the cilium agent resources. @@ -215,6 +227,22 @@ agent: true name: cilium # -- Roll out cilium agent pods automatically when configmap is updated. rollOutCiliumPods: false +# -- Configuration for the ConfigMap drift detection feature. +# When enabled, the agent continuously watches the cilium-config ConfigMap +# and exposes a cilium_drift_checker_config_delta Prometheus metric reporting +# the number of keys that differ between the ConfigMap and the agent's active +# settings. A non-zero value indicates that the agent has not yet applied all +# current ConfigMap changes and needs to be restarted. +configDriftDetection: + # -- Enable watching of the cilium-config ConfigMap and reflecting its + # contents into the agent's internal DynamicConfig table. + enabled: true + # -- Enable the drift checker which compares the DynamicConfig table against + # the agent's active settings and publishes the + # cilium_drift_checker_config_delta metric. + driftChecker: true + # -- List of config-map keys to ignore when computing the drift delta. + ignoredKeys: [] # -- Agent container image. image: # @schema @@ -368,6 +396,8 @@ securityContext: - SETGID # Allow to execute program that changes UID (e.g. required for package installation) - SETUID + # Allow to read dmesg and get kernel pointers when kptr_restrict=1 + - SYSLOG # -- Capabilities for the `mount-cgroup` init container mountCgroup: # Only used for 'mount' cgroup @@ -440,9 +470,16 @@ azure: # clientID: 00000000-0000-0000-0000-000000000000 # clientSecret: 00000000-0000-0000-0000-000000000000 # userAssignedIdentityID: 00000000-0000-0000-0000-000000000000 + nodeSpec: + azureInterfaceName: "" alibabacloud: # -- Enable AlibabaCloud ENI integration enabled: false + nodeSpec: + vSwitches: [] + vSwitchTags: [] + securityGroups: [] + securityGroupTags: [] # -- Enable bandwidth manager to optimize TCP and UDP workloads and allow # for rate-limiting traffic from individual Pods with EDT (Earliest Departure # Time) through the "kubernetes.io/egress-bandwidth" Pod annotation. @@ -475,8 +512,7 @@ l2podAnnouncements: interface: "eth0" # -- A regular expression matching interfaces used for sending Gratuitous ARP pod announcements # interfacePattern: "" -# -- This feature set enables virtual BGP routers to be created via -# CiliumBGPPeeringPolicy CRDs. +# -- This feature set enables virtual BGP routers to be created via BGP CRDs. bgpControlPlane: # -- Enables the BGP control plane. enabled: false @@ -486,9 +522,9 @@ bgpControlPlane: create: false # -- The name of the secret namespace to which Cilium agents are given read access name: kube-system - # -- Status reporting settings (BGPv2 only) + # -- Status reporting settings statusReport: - # -- Enable/Disable BGPv2 status reporting + # -- Enable/Disable BGP status reporting # It is recommended to enable status reporting in general, but if you have any issue # such as high API server load, you can disable it by setting this to false. enabled: true @@ -498,7 +534,7 @@ bgpControlPlane: mode: "default" # -- IP pool to allocate the BGP router-id from when the mode is ip-pool. ipPool: "" - # -- Legacy BGP ORIGIN attribute settings (BGPv2 only) + # -- Legacy BGP ORIGIN attribute settings legacyOriginAttribute: # -- Enable/Disable advertising LoadBalancerIP routes with the legacy # BGP ORIGIN attribute value INCOMPLETE (2) instead of the default IGP (0). @@ -508,6 +544,11 @@ pmtuDiscovery: # -- Enable path MTU discovery to send ICMP fragmentation-needed replies to # the client. enabled: false + # -- Enable kernel probing path MTU discovery for Pods which uses different message + # sizes to search for correct MTU value. + # Valid values are: always, blackhole, disabled and unset (or empty). If value + # is 'unset' or left empty then will not try to override setting. + packetizationLayerPMTUDMode: "blackhole" bpf: autoMount: # -- Enable automatic mount of BPF filesystem @@ -555,7 +596,7 @@ bpf: # Helm configuration for BPF events map rate limiting is experimental and might change # in upcoming releases. events: - # -- Default settings for all types of events except dbg and pcap. + # -- Default settings for all types of events except dbg. default: # @schema # type: [null, integer] @@ -615,6 +656,12 @@ bpf: # type: [null, integer] # @schema policyMapMax: 16384 + # -- (float64) Configure threshold for emitting pressure metrics of policy maps. + # @schema + # type: [null, number] + # @schema + # @default -- `0.1` + policyMapPressureMetricsThreshold: ~ # -- Configure the maximum number of entries in global policy stats map. # @schema # type: [null, integer] @@ -672,7 +719,8 @@ bpf: # type: [null, boolean] # @schema # -- (bool) Configure the eBPF-based TPROXY (beta) to reduce reliance on iptables rules - # for implementing Layer 7 policy. + # for implementing Layer 7 policy. Note this is incompatible with netkit (`bpf.datapathMode=netkit`, + # `bpf.datapathMode=netkit-l2`). # @default -- `false` tproxy: ~ # @schema @@ -682,6 +730,15 @@ bpf: # [0] will allow all VLAN id's without any filtering. # @default -- `[]` vlanBypass: ~ + # -- Configure the IP tracing option type. + # This option is used to specify the IP option type to use for tracing. + # The value must be an integer between 0 and 255. + # @schema + # type: [null, integer] + # minimum: 0 + # maximum: 255 + # @schema + monitorTraceIPOption: 0 # -- (bool) Disable ExternalIP mitigation (CVE-2020-8554) # @default -- `false` disableExternalIPMitigation: false @@ -689,7 +746,8 @@ bpf: # supported kernels. # @default -- `true` enableTCX: true - # -- (string) Mode for Pod devices for the core datapath (veth, netkit, netkit-l2) + # -- (string) Mode for Pod devices for the core datapath (veth, netkit, netkit-l2). + # Note netkit is incompatible with TPROXY (`bpf.tproxy`). # @default -- `veth` datapathMode: veth # -- Enable BPF clock source probing for more efficient tick retrieval. @@ -778,8 +836,17 @@ cni: # -- Specifies the resources for the cni initContainer resources: requests: + # @schema + # type: [integer, string] + # @schema cpu: 100m memory: 10Mi + limits: + # @schema + # type: [integer, string] + # @schema + cpu: 1 + memory: 1Gi # -- Enable route MTU for pod netns when CNI chaining is used enableRouteMTUForCNIChaining: false # -- Enable the removal of iptables rules created by the AWS CNI VPC plugin. @@ -804,10 +871,6 @@ conntrackGCMaxInterval: "" # -- (string) Configure timeout in which Cilium will exit if CRDs are not available # @default -- `"5m"` crdWaitTimeout: "" -# -- Tail call hooks for custom eBPF programs. -customCalls: - # -- Enable tail call hooks for custom eBPF programs. - enabled: false daemon: # -- Configure where Cilium runtime state should be stored. runPath: "/var/run/cilium" @@ -850,6 +913,13 @@ daemon: # # By default, this functionality is enabled enableSourceIPVerification: true + +# -- Configure temporary volume for cilium-agent +tmpVolume: {} + # emptyDir: + # sizeLimit: "100Mi" + # medium: "Memory" + # -- Specify which network interfaces can run the eBPF datapath. This means # that a packet sent from a pod to a destination outside the cluster will be # masqueraded (to an output device IPv4 address), if the output device runs the @@ -869,9 +939,6 @@ forceDeviceDetection: false # -- Enable setting identity mark for local traffic. # enableIdentityMark: true -# -- Enable Kubernetes EndpointSlice feature in Cilium if the cluster supports it. -# enableK8sEndpointSlice: true - # -- CiliumEndpointSlice configuration options. ciliumEndpointSlice: # -- Enable Cilium EndpointSlice feature. @@ -1056,20 +1123,33 @@ enableXTSocketFallback: true encryption: # -- Enable transparent network encryption. enabled: false - # -- Encryption method. Can be either ipsec or wireguard. + # -- Encryption method. Can be one of ipsec, wireguard or ztunnel. type: ipsec # -- Enable encryption for pure node to node traffic. # This option is only effective when encryption.type is set to "wireguard". nodeEncryption: false - # -- Configure the WireGuard Pod2Pod strict mode. + # -- Configure the Encryption Pod2Pod strict mode. strictMode: - # -- Enable WireGuard Pod2Pod strict mode. + # -- Enable Encryption Pod2Pod strict mode. (deprecated: please use encryption.strictMode.egress.enabled) enabled: false - # -- CIDR for the WireGuard Pod2Pod strict mode. + # -- CIDR for the Encryption Pod2Pod strict mode. (deprecated: please use encryption.strictMode.egress.cidr) cidr: "" - # -- Allow dynamic lookup of remote node identities. + # -- Allow dynamic lookup of remote node identities. (deprecated: please use encryption.strictMode.egress.allowRemoteNodeIdentities) # This is required when tunneling is used or direct routing is used and the node CIDR and pod CIDR overlap. allowRemoteNodeIdentities: false + egress: + # -- Enable strict egress encryption. + enabled: false + # -- CIDR for the Encryption Pod2Pod strict egress mode. + cidr: "" + # -- Allow dynamic lookup of remote node identities. + # This is required when tunneling is used or direct routing is used and the node CIDR and pod CIDR overlap. + allowRemoteNodeIdentities: false + ingress: + # -- Enable strict ingress encryption. + # When enabled, all unencrypted overlay ingress traffic will be dropped. + # This option is only applicable when WireGuard and tunneling are enabled. + enabled: false ipsec: # -- Name of the key file inside the Kubernetes secret configured via secretName. keyFile: keys @@ -1090,9 +1170,89 @@ encryption: wireguard: # -- Controls WireGuard PersistentKeepalive option. Set 0s to disable. persistentKeepalive: 0s + # -- ztunnel encryption configuration. + # ztunnel is Istio's purpose-built, per-node proxy for handling L4 traffic in ambient mesh mode. + # These settings only apply when encryption.type is set to "ztunnel". + ztunnel: + # -- ztunnel container image. + image: + # @schema + # type: [null, string] + # @schema + override: ~ + repository: "docker.io/istio/ztunnel" + tag: "1.28.0-distroless" + pullPolicy: "IfNotPresent" + # @schema + # type: [null, string] + # @schema + digest: ~ + useDigest: false + # -- CA server address for certificate requests. + caAddress: "https://localhost:15012" + # -- TCP port for the health API. + healthPort: 15021 + # -- ztunnel resource limits & requests. + resources: + requests: + cpu: 200m + memory: 512Mi + # -- ztunnel update strategy. + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 + # -- Configure termination grace period for ztunnel DaemonSet. + terminationGracePeriodSeconds: 30 + # -- Readiness probe configuration. + readinessProbe: + initialDelaySeconds: 0 + periodSeconds: 10 + failureThreshold: 3 + # -- Node selector for ztunnel pods. + nodeSelector: + kubernetes.io/os: linux + # -- Node tolerations for ztunnel scheduling. + tolerations: + - effect: NoSchedule + operator: Exists + - key: CriticalAddonsOnly + operator: Exists + - effect: NoExecute + operator: Exists + # -- Affinity for ztunnel pods. + affinity: {} + # @schema + # type: [null, string] + # @schema + # -- The priority class to use for ztunnel pods. + priorityClassName: ~ + # -- Annotations to be added to all ztunnel resources. + annotations: {} + # -- Annotations to be added to ztunnel pods. + podAnnotations: {} + # -- Labels to be added to ztunnel pods. + podLabels: {} + # -- Additional ztunnel container environment variables. + extraEnv: [] + # -- Additional ztunnel volumes. + extraVolumes: [] + # -- Additional ztunnel volumeMounts. + extraVolumeMounts: [] + # -- ztunnel secrets configuration. + secrets: + # -- Base64-encoded bootstrap root certificate content. + # If not provided, the secret must be created manually before deploying. + # @schema + # type: [null, string] + # @schema + bootstrapRootCert: ~ endpointHealthChecking: # -- Enable connectivity health checking between virtual endpoints. enabled: true +# -- Max duration to wait for envoy to respond to configuration changes. Default "10s". +endpointPolicyUpdateTimeoutDuration: null endpointRoutes: # @schema # type: [boolean, string] @@ -1141,6 +1301,33 @@ eni: # -- Filter via AWS EC2 Instance tags (k=v) which will dictate which AWS EC2 Instances # are going to be used to create new ENIs instanceTagsFilter: [] + # -- NodeSpec configuration for the ENI + nodeSpec: + # -- First interface index to use for IP allocation + # @schema + # type: [null, integer] + # @schema + firstInterfaceIndex: ~ + # -- Subnet IDs to use for IP allocation + subnetIDs: [] + # -- Subnet tags to use for IP allocation + subnetTags: [] + # -- Security groups to use for IP allocation + securityGroups: [] + # -- Security group tags to use for IP allocation + securityGroupTags: [] + # -- Exclude interface tags to use for IP allocation + excludeInterfaceTags: [] + # -- Use primary address for IP allocation + usePrimaryAddress: false + # -- Disable prefix delegation for IP allocation + disablePrefixDelegation: false + # -- Delete ENI on termination + # @schema + # type: [null, boolean] + # @schema + deleteOnTermination: ~ + # fragmentTracking enables IPv4 fragment tracking support in the datapath. # fragmentTracking: true gke: @@ -1156,6 +1343,8 @@ healthCheckICMPFailureThreshold: 3 hostFirewall: # -- Enables the enforcement of host policies in the eBPF datapath. enabled: false +# -- Enable routing to a service that has zero endpoints +enableNoServiceEndpointsRoutable: true # -- Configure socket LB socketLB: # -- Enable socket LB @@ -1183,8 +1372,11 @@ certgen: digest: "${CERTGEN_DIGEST}" useDigest: true pullPolicy: "${PULL_POLICY}" + # @schema + # type: [null, integer] + # @schema # -- Seconds after which the completed job pod will be deleted - ttlSecondsAfterFinished: 1800 + ttlSecondsAfterFinished: null # -- Labels to be added to hubble-certgen pods podLabels: {} # -- Annotations to be added to the hubble-certgen initial Job and CronJob @@ -1209,6 +1401,11 @@ certgen: extraVolumeMounts: [] # -- Affinity for certgen affinity: {} + cronJob: + # -- The number of successful finished jobs to keep + successfulJobsHistoryLimit: 3 + # -- The number of failed finished jobs to keep + failedJobsHistoryLimit: 1 hubble: # -- Enable Hubble (true by default). enabled: true @@ -1224,6 +1421,9 @@ hubble: # 2047, 4095, 8191, 16383, 32767, 65535 # eventBufferCapacity: "4095" + # -- The interval at which Hubble will send out lost events from the Observer server, if any. + # lostEventSendInterval: 1s + # -- Hubble metrics configuration. # See https://docs.cilium.io/en/stable/observability/metrics/#hubble-metrics # for more comprehensive documentation about Hubble metrics. @@ -1730,6 +1930,24 @@ hubble: address: localhost # -- Configure pprof listen port for hubble-relay port: 6062 + # -- Enable mutex contention profiling for hubble-relay and set the fraction of sampled events (set to 1 to sample all events) + mutexProfileFraction: 0 + # -- Enable goroutine blocking profiling for hubble-relay and set the rate of sampled events in nanoseconds (set to 1 to sample all events [warning: performance overhead]) + blockProfileRate: 0 + # -- Logging configuration for hubble-relay. + logOptions: + # @schema + # type: [null, string] + # @schema + # -- Log format for hubble-relay. Valid values are: text, text-ts, json, json-ts. + # @default -- text-ts + format: ~ + # @schema + # type: [null, string] + # @schema + # -- Log level for hubble-relay. Valid values are: debug, info, warn, error. + # @default -- info + level: ~ ui: # -- Whether to enable the Hubble UI. enabled: false @@ -1925,6 +2143,13 @@ hubble: # - secretName: chart-example-tls # hosts: # - chart-example.local + + # -- Configure temporary volume for hubble-ui + tmpVolume: {} + # emptyDir: + # # sizeLimit: "100Mi" + # # medium: "Memory" + # -- Hubble flows export. export: # --- Static exporter configuration. @@ -1937,6 +2162,14 @@ hubble: # - source # - destination # - verdict + fieldAggregate: [] + # - time + # - source + # - destination + # - verdict + # --- Defines the interval at which to aggregate before exporting Hubble flows. + # Aggregation feature is only enabled when fieldAggregate is specified and aggregationInterval > 0s. + aggregationInterval: "0s" allowList: [] # - '{"verdict":["DROPPED","ERROR"]}' denyList: [] @@ -1962,6 +2195,8 @@ hubble: content: - name: all fieldMask: [] + fieldAggregate: [] + aggregationInterval: "0s" includeFilters: [] excludeFilters: [] filePath: "/var/run/cilium/hubble/events.log" @@ -2056,11 +2291,30 @@ ipam: # refill the bucket up to the burst size capacity. # @default -- `4.0` externalAPILimitQPS: ~ -# -- defaultLBServiceIPAM indicates the default LoadBalancer Service IPAM when -# no LoadBalancer class is set. Applicable values: lbipam, nodeipam, none + # -- NodeSpec configuration for the IPAM + nodeSpec: + # -- IPAM min allocate + # @schema + # type: [null, integer] + # @schema + ipamMinAllocate: ~ + # -- IPAM pre allocate + # @schema + # type: [null, integer] + # @schema + ipamPreAllocate: ~ + # -- IPAM max allocate + # @schema + # type: [null, integer] + # @schema + ipamMaxAllocate: ~ + # -- IPAM static IP tags (currently only works with AWS and Azure) + ipamStaticIPTags: [] # @schema # type: [string] # @schema +# -- defaultLBServiceIPAM indicates the default LoadBalancer Service IPAM when +# no LoadBalancer class is set. Applicable values: lbipam, nodeipam, none defaultLBServiceIPAM: lbipam nodeIPAM: # -- Configure Node IPAM @@ -2147,7 +2401,7 @@ localRedirectPolicy: false localRedirectPolicies: # -- Enable local redirect policies. enabled: false - + # -- Limit the allowed addresses in Address Matcher rule of # Local Redirect Policies to the given CIDRs. # @schema@ @@ -2177,7 +2431,7 @@ maglev: {} # type: [null, boolean] # @schema # -- (bool) Enables masquerading of IPv4 traffic leaving the node from endpoints. -# @default -- `true` unless ipam eni mode is active +# @default -- `true` unless ipam eni mode is active enableIPv4Masquerade: ~ # -- Enables masquerading of IPv6 traffic leaving the node from endpoints. enableIPv6Masquerade: true @@ -2266,7 +2520,7 @@ loadBalancer: # -- serviceTopology enables K8s Topology Aware Hints -based service # endpoints filtering - # serviceTopology: false + serviceTopology: false # -- L7 LoadBalancer l7: @@ -2290,8 +2544,6 @@ loadBalancer: algorithm: round_robin # -- Configure N-S k8s service loadbalancing nodePort: - # -- Enable the Cilium NodePort service implementation. - enabled: false # -- Port range to use for NodePort services. # range: "30000,32767" @@ -2336,6 +2588,10 @@ pprof: address: localhost # -- Configure pprof listen port for cilium-agent port: 6060 + # -- Enable mutex contention profiling for cilium-agent and set the fraction of sampled events (set to 1 to sample all events) + mutexProfileFraction: 0 + # -- Enable goroutine blocking profiling for cilium-agent and set the rate of sampled events in nanoseconds (set to 1 to sample all events [warning: performance overhead]) + blockProfileRate: 0 # -- Configure prometheus metrics on the configured port at /metrics prometheus: metricsService: false @@ -2460,6 +2716,12 @@ envoy: initialFetchTimeoutSeconds: 30 # -- Maximum number of concurrent retries on Envoy clusters maxConcurrentRetries: 128 + # -- Maximum number of connections on Envoy clusters + clusterMaxConnections: 1024 + # -- Maximum number of requests on Envoy clusters + clusterMaxRequests: 1024 + # -- Maximum number of global downstream connections + maxGlobalDownstreamConnections: 50000 # -- Maximum number of retries for each HTTP request httpRetryCount: 3 # -- ProxyMaxRequestsPerConnection specifies the max_requests_per_connection setting for Envoy @@ -2476,6 +2738,9 @@ envoy: xffNumTrustedHopsL7PolicyIngress: 0 # -- Number of trusted hops regarding the x-forwarded-for and related HTTP headers for the egress L7 policy enforcement Envoy listeners. xffNumTrustedHopsL7PolicyEgress: 0 + # -- For cases when CiliumEnvoyConfig is not used directly (Ingress, Gateway), configures Cilium BPF Metadata listener filter + # to use the original source address when extracting the metadata for a request. + useOriginalSourceAddress: true # @schema # type: [null, string] # @schema @@ -2494,6 +2759,8 @@ envoy: pullPolicy: "${PULL_POLICY}" digest: "${CILIUM_ENVOY_DIGEST}" useDigest: true + # -- Init containers added to the cilium Envoy DaemonSet. + initContainers: [] # -- Additional containers added to the cilium Envoy DaemonSet. extraContainers: [] # -- Additional envoy container arguments. @@ -2726,17 +2993,16 @@ resourceQuotas: pods: "15" # Need to document default ################## -#sessionAffinity: false # -- Annotations to be added to all cilium-secret namespaces (resources under templates/cilium-secrets-namespace) secretsNamespaceAnnotations: {} +# -- Labels to be added to all cilium-secret namespaces (resources under templates/cilium-secrets-namespace) +secretsNamespaceLabels: {} # -- Do not run Cilium agent when running with clean mode. Useful to completely # uninstall Cilium as it will stop Cilium from starting and create artifacts # in the node. sleepAfterInit: false -# -- Enable check of service source ranges (currently, only for LoadBalancer). -svcSourceRangeCheck: true # -- Synchronize Kubernetes nodes to kvstore and perform CNP GC. synchronizeK8sNodes: true # -- Configure TLS configuration in the agent. @@ -2819,6 +3085,9 @@ tls: # @default -- `"vxlan"` tunnelProtocol: "" # -- IP family for the underlay. +# Possible values: +# - "ipv4" +# - "ipv6" # @default -- `"ipv4"` underlayProtocol: "" # -- Enable native-routing mode or tunneling mode. @@ -2839,6 +3108,11 @@ tunnelSourcePortRange: 0-0 # - reject (default) # - drop serviceNoBackendResponse: reject +# -- Configure what the response should be to pod egress traffic denied by network policy. +# Possible values: +# - none (default) +# - icmp +policyDenyResponse: none # -- Configure the underlying network MTU to overwrite auto-detected MTU. # This value doesn't change the host network interface MTU i.e. eth0 or ens0. # It changes the MTU for cilium_net@cilium_host, cilium_host@cilium_net, @@ -2924,7 +3198,7 @@ operator: # @schema # type: [null, array] # @schema - tolerations: + tolerations: - key: "node-role.kubernetes.io/control-plane" operator: Exists - key: "node-role.kubernetes.io/master" #deprecated @@ -3016,6 +3290,10 @@ operator: address: localhost # -- Configure pprof listen port for cilium-operator port: 6061 + # -- Enable mutex contention profiling for cilium-operator and set the fraction of sampled events (set to 1 to sample all events) + mutexProfileFraction: 0 + # -- Enable goroutine blocking profiling for cilium-operator and set the rate of sampled events in nanoseconds (set to 1 to sample all events [warning: performance overhead]) + blockProfileRate: 0 # -- Enable prometheus metrics for cilium-operator on the configured port at # /metrics prometheus: @@ -3049,6 +3327,17 @@ operator: # @schema # -- Metrics relabeling configs for the ServiceMonitor cilium-operator metricRelabelings: ~ + # -- TLS configuration for Prometheus + tls: + enabled: false + server: + # -- Name of the Secret containing the certificate, key and CA files for the Prometheus server. + existingSecret: "" + mtls: + # When set to true enforces mutual TLS between Operator Prometheus server and its clients. + # False allow non-mutual TLS connections. + # This option has no effect when TLS is disabled. + enabled: false # -- Grafana dashboards for cilium-operator # grafana can import dashboards based on the label and value # ref: https://github.com/grafana/helm-charts/tree/main/charts/grafana#sidecar-for-dashboards @@ -3082,6 +3371,12 @@ operator: # -- Interval, in seconds, to check if there are any pods that are not # managed by Cilium. intervalSeconds: 15 + # -- Selector for pods that should be restarted when not managed by Cilium. + # If not set, defaults to built-in selector "k8s-app=kube-dns". Set to empty string to select all pods. + # @schema + # type: [null, string] + # @schema + selector: ~ nodeinit: # -- Enable the node initialization DaemonSet enabled: false @@ -3136,6 +3431,9 @@ nodeinit: # ref: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ resources: requests: + # @schema + # type: [integer, string] + # @schema cpu: 100m memory: 100Mi # -- Security context to be added to nodeinit pods. @@ -3160,6 +3458,8 @@ nodeinit: # -- bootstrapFile is the location of the file where the bootstrap timestamp is # written by the node-init DaemonSet bootstrapFile: "/tmp/cilium-bootstrap.d/cilium-bootstrap-time" + # -- wait for Cloud init to finish on the host and assume the node has cloud init installed + waitForCloudInit: false # -- startup offers way to customize startup nodeinit script (pre and post position) startup: preScript: "" @@ -3293,7 +3593,9 @@ enableCriticalPriorityClass: true # on AArch64 as the images do not currently ship a version of Envoy. #disableEnvoyVersionCheck: false clustermesh: - # -- Deploy clustermesh-apiserver for clustermesh + # -- Deploy clustermesh-apiserver for clustermesh. This option is typically + # used with ``clustermesh.config.enabled=true``. Refer to the + # ``clustermesh.config.enabled=true``documentation for more information. useAPIServer: false # -- The maximum number of clusters to support in a ClusterMesh. This value # cannot be changed on running clusters, and all clusters in a ClusterMesh @@ -3301,45 +3603,133 @@ clustermesh: # maximum allocatable cluster-local identities. # Supported values are 255 and 511. maxConnectedClusters: 255 + # -- The time to live for the cache of a remote cluster after connectivity is + # lost. If the connection is not re-established within this duration, the + # cached data is revoked to prevent stale state. If not specified or set to + # 0s, the cache is never revoked (default). + cacheTTL: "0s" # -- Enable the synchronization of Kubernetes EndpointSlices corresponding to # the remote endpoints of appropriately-annotated global services through ClusterMesh enableEndpointSliceSynchronization: false - # -- Enable Multi-Cluster Services API support + # -- Enable Multi-Cluster Services API support (deprecated; use clustermesh.mcsapi.enabled) enableMCSAPISupport: false # -- Control whether policy rules assume by default the local cluster if not explicitly selected - policyDefaultLocalCluster: false + policyDefaultLocalCluster: true # -- Annotations to be added to all top-level clustermesh objects (resources under templates/clustermesh-apiserver and templates/clustermesh-config) annotations: {} # -- Clustermesh explicit configuration. config: # -- Enable the Clustermesh explicit configuration. + # If set to false, you need to provide the following resources yourself: + # - (Secret) cilium-clustermesh (used by cilium-agent/cilium-operator to connect to + # the local etcd instance if KVStoreMesh is enabled or the remote clusters + # if KVStoreMesh is disabled) + # - (Secret) cilium-kvstoremesh (used by KVStoreMesh to connect to the remote clusters) + # - (ConfigMap) clustermesh-remote-users (used to create one etcd user per remote cluster + # if clustermesh-apiserver is used and `clustermesh.apiserver.tls.authMode` is not + # set to `legacy`) enabled: false # -- Default dns domain for the Clustermesh API servers # This is used in the case cluster addresses are not provided # and IPs are used. domain: mesh.cilium.io - # -- List of clusters to be peered in the mesh. + # -- Clusters to be peered in the mesh. + # @schema + # type: [object, array] + # @schema clusters: [] + # You can use a dict of clusters (recommended): # clusters: - # # -- Name of the cluster + # # -- Name of the cluster + # cluster1: + # # -- Whether to enable this cluster in the mesh. Optional, defaults to true. + # enabled: true + # # -- Address of the cluster, use this if you created DNS records for + # # the cluster Clustermesh API server. + # address: cluster1.mesh.cilium.io + # # -- Port of the cluster Clustermesh API server. + # port: 2379 + # # -- IPs of the cluster Clustermesh API server, use multiple ones when + # # you have multiple IPs to access the Clustermesh API server. + # ips: + # - 172.18.255.201 + # # -- (deprecated) base64 encoded PEM values for the cluster client certificate, private key and certificate authority. + # # These fields can (and should) be omitted in case the CA is shared across clusters. In that case, the + # # "remote" private key and certificate available in the local cluster are automatically used instead. + # tls: + # cert: "" + # key: "" + # caCert: "" + # + # Or alternatively you can use a list of clusters: + # clusters: + # # -- Name of the cluster # - name: cluster1 - # # -- Address of the cluster, use this if you created DNS records for - # # the cluster Clustermesh API server. + # # -- Address of the cluster, use this if you created DNS records for + # # the cluster Clustermesh API server. # address: cluster1.mesh.cilium.io - # # -- Port of the cluster Clustermesh API server. + # # -- Port of the cluster Clustermesh API server. # port: 2379 - # # -- IPs of the cluster Clustermesh API server, use multiple ones when - # # you have multiple IPs to access the Clustermesh API server. + # # -- IPs of the cluster Clustermesh API server, use multiple ones when + # # you have multiple IPs to access the Clustermesh API server. # ips: # - 172.18.255.201 - # # -- base64 encoded PEM values for the cluster client certificate, private key and certificate authority. - # # These fields can (and should) be omitted in case the CA is shared across clusters. In that case, the - # # "remote" private key and certificate available in the local cluster are automatically used instead. + # # -- (deprecated) base64 encoded PEM values for the cluster client certificate, private key and certificate authority. + # # These fields can (and should) be omitted in case the CA is shared across clusters. In that case, the + # # "remote" private key and certificate available in the local cluster are automatically used instead. # tls: # cert: "" # key: "" # caCert: "" + mcsapi: + # -- Enable Multi-Cluster Services API support + enabled: false + # -- Enabled MCS-API CRDs auto-installation + installCRDs: true + corednsAutoConfigure: + # -- Enable auto-configuration of CoreDNS for Multi-Cluster Services API. + # CoreDNS MUST be at least in version v1.12.2 to run this. + enabled: false + coredns: + # -- The Deployment for the cluster CoreDNS service + deploymentName: coredns + # -- The Service Account name for the cluster CoreDNS service + serviceAccountName: coredns + # -- The ConfigMap name for the cluster CoreDNS service + configMapName: coredns + # -- The namespace for the cluster CoreDNS service + namespace: kube-system + # -- The cluster domain for the cluster CoreDNS service + clusterDomain: cluster.local + # -- The clusterset domain for the cluster CoreDNS service + clustersetDomain: clusterset.local + # -- Additional arguments to `clustermesh-apiserver coredns-mcsapi-auto-configure`. + extraArgs: [] + # -- Seconds after which the completed job pod will be deleted + ttlSecondsAfterFinished: 1800 + # -- Labels to be added to coredns-mcsapi-autoconfig pods + podLabels: {} + # -- Annotations to be added to the coredns-mcsapi-autoconfig Job + annotations: {} + # -- Node selector for coredns-mcsapi-autoconfig + # ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector + nodeSelector: {} + # -- Priority class for coredns-mcsapi-autoconfig + # ref: https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/#priorityclass + priorityClassName: "" + # -- Node tolerations for pod assignment on nodes with taints + # ref: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ + tolerations: [] + # -- Resource limits for coredns-mcsapi-autoconfig + # ref: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers + resources: {} + # -- Additional coredns-mcsapi-autoconfig volumes. + extraVolumes: [] + # -- Additional coredns-mcsapi-autoconfig volumeMounts. + extraVolumeMounts: [] + # -- Affinity for coredns-mcsapi-autoconfig + affinity: {} apiserver: # -- Clustermesh API server image. image: @@ -3445,17 +3835,12 @@ clustermesh: # - "external": ``clustermesh-apiserver`` will sync remote cluster information to the etcd used as kvstore. This can't be enabled with crd identity allocation mode. kvstoreMode: "internal" service: + # -- (bool) Set externallyCreated to true to create the clustermesh-apiserver service outside this helm chart. + # For example after external load balancer controllers are created. + externallyCreated: false # -- The type of service used for apiserver access. type: NodePort # -- Optional port to use as the node port for apiserver access. - # - # WARNING: make sure to configure a different NodePort in each cluster if - # kube-proxy replacement is enabled, as Cilium is currently affected by a known - # bug (#24692) when NodePorts are handled by the KPR implementation. If a service - # with the same NodePort exists both in the local and the remote cluster, all - # traffic originating from inside the cluster and targeting the corresponding - # NodePort will be redirected to a local backend, regardless of whether the - # destination node belongs to the local or the remote cluster. nodePort: 32379 # -- Annotations for the clustermesh-apiserver service. # Example annotations to configure an internal load balancer on different cloud providers: @@ -3624,13 +4009,15 @@ clustermesh: # The "remote" certificate must be generated with CN=remote- # if provided manually. Cluster mode is meaningful only when the same # CA is shared across all clusters part of the mesh. - authMode: legacy - # -- Allow users to provide their own certificates + authMode: migration + # -- (deprecated) Allow users to provide their own certificates # Users may need to provide their certificates using # a mechanism that requires they provide their own secrets. # This setting does not apply to any of the auto-generated # mechanisms below, it only restricts the creation of secrets # via the `tls-provided` templates. + # This option is deprecated as secrets are expected to be created + # externally when 'auto' is not enabled. enableSecrets: true # -- Configure automatic TLS certificates generation. # A Kubernetes CronJob is used the generate any @@ -3639,7 +4026,14 @@ clustermesh: auto: # -- When set to true, automatically generate a CA and certificates to # enable mTLS between clustermesh-apiserver and external workload instances. - # If set to false, the certs to be provided by setting appropriate values below. + # + # When set to false you need to pre-create the following secrets: + # - clustermesh-apiserver-server-cert + # - clustermesh-apiserver-admin-cert + # - clustermesh-apiserver-remote-cert + # - clustermesh-apiserver-local-cert + # The above secret should at least contains the keys `tls.crt` and `tls.key` + # and optionally `ca.crt` if a CA bundle is not configured. enabled: true # Sets the method to auto-generate certificates. Supported values: # - helm: This method uses Helm to generate all certificates. @@ -3671,10 +4065,13 @@ clustermesh: # name: ca-issuer # -- certmanager issuer used when clustermesh.apiserver.tls.auto.method=certmanager. certManagerIssuerRef: {} + # -- base64 encoded PEM values for the clustermesh-apiserver server certificate and private key. # Used if 'auto' is not enabled. server: + # -- Deprecated, as secrets will always need to be created externally if `auto` is disabled. cert: "" + # -- Deprecated, as secrets will always need to be created externally if `auto` is disabled. key: "" # -- Extra DNS names added to certificate when it's auto generated extraDnsNames: [] @@ -3683,17 +4080,16 @@ clustermesh: # -- base64 encoded PEM values for the clustermesh-apiserver admin certificate and private key. # Used if 'auto' is not enabled. admin: + # -- Deprecated, as secrets will always need to be created externally if `auto` is disabled. cert: "" - key: "" - # -- base64 encoded PEM values for the clustermesh-apiserver client certificate and private key. - # Used if 'auto' is not enabled. - client: - cert: "" + # -- Deprecated, as secrets will always need to be created externally if `auto` is disabled. key: "" # -- base64 encoded PEM values for the clustermesh-apiserver remote cluster certificate and private key. # Used if 'auto' is not enabled. remote: + # -- Deprecated, as secrets will always need to be created externally if `auto` is disabled. cert: "" + # -- Deprecated, as secrets will always need to be created externally if `auto` is disabled. key: "" # clustermesh-apiserver Prometheus metrics configuration metrics: @@ -3848,7 +4244,7 @@ authentication: # -- Enable authentication processing and garbage collection. # Note that if disabled, policy enforcement will still block requests that require authentication. # But the resulting authentication requests for these requests will not be processed, therefore the requests not be allowed. - enabled: true + enabled: false # -- Buffer size of the channel Cilium uses to receive authentication events from the signal map. queueSize: 1024 # -- Buffer size of the channel Cilium uses to receive certificate expiration events from auth handlers. @@ -4041,4 +4437,41 @@ authentication: enableInternalTrafficPolicy: true # -- Enable LoadBalancer IP Address Management enableLBIPAM: true - +# -- Standalone DNS Proxy Configuration +# Note: The standalone DNS proxy uses the agent's dnsProxy.* configuration +# for DNS settings (proxyPort, enableDnsCompression) to ensure consistency. +standaloneDnsProxy: + # -- Enable standalone DNS proxy (alpha feature) + enabled: false + # -- Roll out Standalone DNS proxy automatically when configmap is updated. + rollOutPods: false + # -- Standalone DNS proxy annotations + annotations: {} + # -- Standalone DNS proxy debug mode + debug: false + # -- Standalone DNS proxy server port + serverPort: 10095 + # -- Standalone DNS proxy Node Selector + nodeSelector: + kubernetes.io/os: linux + # -- Standalone DNS proxy tolerations + tolerations: [] + # -- Standalone DNS proxy auto mount service account token + automountServiceAccountToken: false + # -- Standalone DNS proxy update strategy + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 2 + maxUnavailable: 0 + # -- Standalone DNS proxy image + image: + # @schema + # type: [null, string] + # @schema + override: ~ + repository: "${STANDALONE_DNS_PROXY_REPO}" + tag: "${STANDALONE_DNS_PROXY_VERSION}" + digest: "${STANDALONE_DNS_PROXY_DIGEST}" + useDigest: ${USE_DIGESTS} + pullPolicy: "${PULL_POLICY}" diff --git a/packages/system/cilium/images/cilium/Dockerfile b/packages/system/cilium/images/cilium/Dockerfile index ebe65c83..32fc6fb8 100644 --- a/packages/system/cilium/images/cilium/Dockerfile +++ b/packages/system/cilium/images/cilium/Dockerfile @@ -1,2 +1,2 @@ -ARG VERSION=v1.18.6 +ARG VERSION=v1.19.3 FROM quay.io/cilium/cilium:${VERSION} diff --git a/packages/system/cilium/values-apparmor.yaml b/packages/system/cilium/values-apparmor.yaml new file mode 100644 index 00000000..0712836c --- /dev/null +++ b/packages/system/cilium/values-apparmor.yaml @@ -0,0 +1,20 @@ +cilium: + # Opt out of the cri-containerd.apparmor.d profile for containers that invoke + # nsenter to join the host cgroup/mount namespace. The kernel reports the + # denial as a blocked "ptrace" operation. Required on Ubuntu 22.04+ and + # other distros that load this AppArmor profile by default, where the + # denial otherwise puts cilium-agent into Init:CrashLoopBackOff. + # The deprecated annotations are used because k8s >= 1.30 pod-level + # appArmorProfile: Unconfined is not honoured by containerd CRI today + # (kubernetes/kubernetes#125069). + # Only applied on non-Talos cilium variants where cgroup.autoMount.enabled is + # true — on Talos mount-cgroup is not rendered and the kube-apiserver would + # reject an annotation referencing a non-existent container. + # mount-bpf-fs is intentionally excluded: it renders with its own + # securityContext.privileged=true, and the kernel does not apply AppArmor + # profiles to privileged containers. + podAnnotations: + container.apparmor.security.beta.kubernetes.io/cilium-agent: unconfined + container.apparmor.security.beta.kubernetes.io/clean-cilium-state: unconfined + container.apparmor.security.beta.kubernetes.io/mount-cgroup: unconfined + container.apparmor.security.beta.kubernetes.io/apply-sysctl-overwrites: unconfined diff --git a/packages/system/cilium/values.yaml b/packages/system/cilium/values.yaml index 1d94b1cf..9aba7cb9 100644 --- a/packages/system/cilium/values.yaml +++ b/packages/system/cilium/values.yaml @@ -15,8 +15,8 @@ cilium: mode: "kubernetes" image: repository: ghcr.io/cozystack/cozystack/cilium - tag: 1.18.6 - digest: "sha256:4f4585f8adc3b8becd15d3999f3900a4d3d650f2ab7f85ca8c661f3807113d01" + tag: 1.19.3 + digest: "sha256:700f06f4803a838a8e830be5ace4650e3ad82bdefabfb2f4d110368d307a5efb" envoy: enabled: false rollOutCiliumPods: true diff --git a/packages/system/clickhouse-rd/cozyrds/clickhouse.yaml b/packages/system/clickhouse-rd/cozyrds/clickhouse.yaml index eb6c67f6..3655a8db 100644 --- a/packages/system/clickhouse-rd/cozyrds/clickhouse.yaml +++ b/packages/system/clickhouse-rd/cozyrds/clickhouse.yaml @@ -8,7 +8,7 @@ spec: singular: clickhouse plural: clickhouses openAPISchema: |- - {"title":"Chart Values","type":"object","properties":{"backup":{"description":"Backup configuration.","type":"object","default":{},"required":["cleanupStrategy","enabled","resticPassword","s3AccessKey","s3Bucket","s3Region","s3SecretKey","schedule"],"properties":{"cleanupStrategy":{"description":"Retention strategy for cleaning up old backups.","type":"string","default":"--keep-last=3 --keep-daily=3 --keep-within-weekly=1m"},"enabled":{"description":"Enable regular backups (default: false).","type":"boolean","default":false},"resticPassword":{"description":"Password for Restic backup encryption.","type":"string","default":""},"s3AccessKey":{"description":"Access key for S3 authentication.","type":"string","default":""},"s3Bucket":{"description":"S3 bucket used for storing backups.","type":"string","default":"s3.example.org/clickhouse-backups"},"s3Region":{"description":"AWS S3 region where backups are stored.","type":"string","default":"us-east-1"},"s3SecretKey":{"description":"Secret key for S3 authentication.","type":"string","default":""},"schedule":{"description":"Cron schedule for automated backups.","type":"string","default":"0 2 * * *"}}},"clickhouseKeeper":{"description":"ClickHouse Keeper configuration.","type":"object","default":{},"properties":{"enabled":{"description":"Deploy ClickHouse Keeper for cluster coordination.","type":"boolean","default":true},"replicas":{"description":"Number of Keeper replicas.","type":"integer","default":3},"resourcesPreset":{"description":"Default sizing preset.","type":"string","default":"micro","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"size":{"description":"Persistent Volume Claim size available for application data.","default":"1Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"logStorageSize":{"description":"Size of Persistent Volume for logs.","default":"2Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"logTTL":{"description":"TTL (expiration time) for `query_log` and `query_thread_log`.","type":"integer","default":15},"replicas":{"description":"Number of ClickHouse replicas.","type":"integer","default":2},"resources":{"description":"Explicit CPU and memory configuration for each ClickHouse replica. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"small","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"shards":{"description":"Number of ClickHouse shards.","type":"integer","default":1},"size":{"description":"Persistent Volume Claim size available for application data.","default":"10Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":""},"users":{"description":"Users configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"password":{"description":"Password for the user.","type":"string"},"readonly":{"description":"User is readonly (default: false).","type":"boolean"}}}}}} + {"title":"Chart Values","type":"object","properties":{"replicas":{"description":"Number of ClickHouse replicas.","type":"integer","default":2},"shards":{"description":"Number of ClickHouse shards.","type":"integer","default":1},"resources":{"description":"Explicit CPU and memory configuration for each ClickHouse replica. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"small","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"size":{"description":"Persistent Volume Claim size available for application data.","default":"10Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":""},"logStorageSize":{"description":"Size of Persistent Volume for logs.","default":"2Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"logTTL":{"description":"TTL (expiration time) for `query_log` and `query_thread_log`.","type":"integer","default":15},"users":{"description":"Users configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"password":{"description":"Password for the user.","type":"string"},"readonly":{"description":"User is readonly (default: false).","type":"boolean"}}}},"backup":{"description":"Backup configuration.","type":"object","default":{},"required":["cleanupStrategy","enabled","resticPassword","s3AccessKey","s3Bucket","s3Region","s3SecretKey","schedule"],"properties":{"cleanupStrategy":{"description":"Retention strategy for cleaning up old backups.","type":"string","default":"--keep-last=3 --keep-daily=3 --keep-within-weekly=1m"},"enabled":{"description":"Enable regular backups (default: false).","type":"boolean","default":false},"resticPassword":{"description":"Password for Restic backup encryption.","type":"string","default":""},"s3AccessKey":{"description":"Access key for S3 authentication.","type":"string","default":""},"s3Bucket":{"description":"S3 bucket used for storing backups.","type":"string","default":"s3.example.org/clickhouse-backups"},"s3Region":{"description":"AWS S3 region where backups are stored.","type":"string","default":"us-east-1"},"s3SecretKey":{"description":"Secret key for S3 authentication.","type":"string","default":""},"schedule":{"description":"Cron schedule for automated backups.","type":"string","default":"0 2 * * *"}}},"clickhouseKeeper":{"description":"ClickHouse Keeper configuration.","type":"object","default":{},"properties":{"enabled":{"description":"Deploy ClickHouse Keeper for cluster coordination.","type":"boolean","default":true},"replicas":{"description":"Number of Keeper replicas.","type":"integer","default":3},"resourcesPreset":{"description":"Default sizing preset.","type":"string","default":"micro","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"size":{"description":"Persistent Volume Claim size available for application data.","default":"1Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}}}} release: prefix: clickhouse- labels: diff --git a/packages/system/cozy-proxy/charts/cozy-proxy/Chart.yaml b/packages/system/cozy-proxy/charts/cozy-proxy/Chart.yaml index 13352680..58f166f6 100644 --- a/packages/system/cozy-proxy/charts/cozy-proxy/Chart.yaml +++ b/packages/system/cozy-proxy/charts/cozy-proxy/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: v2 name: cozy-proxy description: A simple kube-proxy addon for 1:1 NAT services in Kubernetes using an NFT backend type: application -version: 0.2.0 -appVersion: 0.2.0 +version: 0.3.0 +appVersion: 0.3.0 diff --git a/packages/system/cozy-proxy/charts/cozy-proxy/values.yaml b/packages/system/cozy-proxy/charts/cozy-proxy/values.yaml index 8cde5bed..e143e926 100644 --- a/packages/system/cozy-proxy/charts/cozy-proxy/values.yaml +++ b/packages/system/cozy-proxy/charts/cozy-proxy/values.yaml @@ -1,6 +1,6 @@ image: repository: ghcr.io/cozystack/cozystack/cozy-proxy - tag: v0.2.0 + tag: v0.3.0 pullPolicy: IfNotPresent daemonset: diff --git a/packages/system/cozystack-api/templates/certmanager.yaml b/packages/system/cozystack-api/templates/certmanager.yaml index def27bd1..4f73768a 100644 --- a/packages/system/cozystack-api/templates/certmanager.yaml +++ b/packages/system/cozystack-api/templates/certmanager.yaml @@ -18,6 +18,8 @@ spec: issuerRef: name: cozystack-api-selfsigned isCA: true + privateKey: + rotationPolicy: Never --- apiVersion: cert-manager.io/v1 kind: Issuer diff --git a/packages/system/cozystack-api/values.yaml b/packages/system/cozystack-api/values.yaml index cc6279bc..dddabf54 100644 --- a/packages/system/cozystack-api/values.yaml +++ b/packages/system/cozystack-api/values.yaml @@ -1,3 +1,3 @@ cozystackAPI: - image: ghcr.io/cozystack/cozystack/cozystack-api:v1.0.0-beta.6@sha256:d89cf68fb622d0dbef7db98db09d352efc93c2cce448d11f2d73dcd363e911b7 + image: ghcr.io/cozystack/cozystack/cozystack-api:v1.3.0@sha256:5fa8648821cf1e9e08cf7c2899c4b1c4226bb74c6773327141456c7d3f2b9b7e replicas: 2 diff --git a/packages/system/cozystack-basics/templates/clusterroles.yaml b/packages/system/cozystack-basics/templates/clusterroles.yaml index 29395dfe..f270f4e1 100644 --- a/packages/system/cozystack-basics/templates/clusterroles.yaml +++ b/packages/system/cozystack-basics/templates/clusterroles.yaml @@ -21,6 +21,9 @@ rules: - apiGroups: [""] resources: ["pods", "services", "persistentvolumes", "endpoints", "events", "resourcequotas"] verbs: ["get", "list", "watch"] +- apiGroups: ["discovery.k8s.io"] + resources: ["endpointslices"] + verbs: ["get", "list", "watch"] - apiGroups: ["networking.k8s.io"] resources: ["ingresses"] verbs: ["get", "list", "watch"] @@ -94,6 +97,14 @@ rules: - get - list - watch +- apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - get + - list + - watch - apiGroups: - networking.k8s.io resources: @@ -181,7 +192,6 @@ rules: - persistentvolumes - endpoints - events - - resourcequotas verbs: - delete - apiGroups: ["kubevirt.io"] @@ -195,12 +205,18 @@ rules: - buckets - clickhouses - foos + - foundationdbs + - harbors - httpcaches - kafkas - kuberneteses - mariadbs + - mongodbs - natses + - openbaos + - opensearches - postgreses + - qdrants - rabbitmqs - redises - seaweedfses @@ -208,6 +224,7 @@ rules: - virtualmachines - vmdisks - vminstances + - vpns - infos - virtualprivateclouds verbs: diff --git a/packages/system/cozystack-basics/templates/monitoring-external-services.yaml b/packages/system/cozystack-basics/templates/monitoring-external-services.yaml index 0f8b4d5c..e36540c4 100644 --- a/packages/system/cozystack-basics/templates/monitoring-external-services.yaml +++ b/packages/system/cozystack-basics/templates/monitoring-external-services.yaml @@ -2,11 +2,11 @@ apiVersion: v1 kind: Service metadata: - name: vlogs-generic + name: vlinsert-generic namespace: cozy-monitoring spec: type: ExternalName - externalName: vlogs-generic.tenant-root.svc.cluster.local + externalName: vlinsert-generic.tenant-root.svc.cluster.local --- apiVersion: v1 kind: Service diff --git a/packages/system/cozystack-basics/templates/tenant-root.yaml b/packages/system/cozystack-basics/templates/tenant-root.yaml index 95af9d7f..93f5129e 100644 --- a/packages/system/cozystack-basics/templates/tenant-root.yaml +++ b/packages/system/cozystack-basics/templates/tenant-root.yaml @@ -23,7 +23,6 @@ spec: namespace: cozy-system interval: 1m0s timeout: 5m0s - values: - _cluster: - oidc-enabled: {{ .Values.oidcEnabled | quote }} - root-host: {{ .Values.rootHost | quote }} + valuesFrom: + - kind: Secret + name: cozystack-values diff --git a/packages/system/cozystack-controller/values.yaml b/packages/system/cozystack-controller/values.yaml index 7a17c992..4d0d0fde 100644 --- a/packages/system/cozystack-controller/values.yaml +++ b/packages/system/cozystack-controller/values.yaml @@ -1,4 +1,4 @@ cozystackController: - image: ghcr.io/cozystack/cozystack/cozystack-controller:v1.0.0-beta.6@sha256:d55a3c288934b1f69a00321bc8a94776915556b5f882fe6ac615e9de2701c61f + image: ghcr.io/cozystack/cozystack/cozystack-controller:v1.3.0@sha256:d03d19b78c4c98f970ac549a68b01ef6bd1ad755f5e0dcb9e08503511cdf2fdc debug: false disableTelemetry: false diff --git a/packages/system/cozystack-scheduler/Chart.yaml b/packages/system/cozystack-scheduler/Chart.yaml new file mode 100644 index 00000000..f2dd13ee --- /dev/null +++ b/packages/system/cozystack-scheduler/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: cozy-cozystack-scheduler +version: 0.3.0 diff --git a/packages/system/cozystack-scheduler/Makefile b/packages/system/cozystack-scheduler/Makefile new file mode 100644 index 00000000..d075ec63 --- /dev/null +++ b/packages/system/cozystack-scheduler/Makefile @@ -0,0 +1,14 @@ +export NAME=cozystack-scheduler +export NAMESPACE=kube-system + +include ../../../hack/package.mk + +test: + helm unittest . + +update: + rm -rf charts + tag=$$(git ls-remote --tags --sort="v:refname" https://github.com/cozystack/cozystack-scheduler | awk -F'[/^]' 'END{print $$3}') && \ + mkdir -p charts/cozystack-scheduler && \ + curl -sSL https://github.com/cozystack/cozystack-scheduler/archive/refs/tags/$${tag}.tar.gz | \ + tar xzvf - --strip 2 -C charts/cozystack-scheduler cozystack-scheduler-$${tag#*v}/chart diff --git a/packages/system/cozystack-scheduler/charts/cozystack-scheduler/Chart.yaml b/packages/system/cozystack-scheduler/charts/cozystack-scheduler/Chart.yaml new file mode 100644 index 00000000..f2dd13ee --- /dev/null +++ b/packages/system/cozystack-scheduler/charts/cozystack-scheduler/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: cozy-cozystack-scheduler +version: 0.3.0 diff --git a/packages/system/cozystack-scheduler/charts/cozystack-scheduler/crds/cozystack.io_schedulingclasses.yaml b/packages/system/cozystack-scheduler/charts/cozystack-scheduler/crds/cozystack.io_schedulingclasses.yaml new file mode 100644 index 00000000..d6b60d8b --- /dev/null +++ b/packages/system/cozystack-scheduler/charts/cozystack-scheduler/crds/cozystack.io_schedulingclasses.yaml @@ -0,0 +1,1123 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.19.0 + name: schedulingclasses.cozystack.io +spec: + group: cozystack.io + names: + kind: SchedulingClass + listKind: SchedulingClassList + plural: schedulingclasses + singular: schedulingclass + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + properties: + nodeAffinity: + description: Node affinity is a group of node affinity scheduling + rules. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with the corresponding + weight. + properties: + matchExpressions: + description: A list of node selector requirements by + node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies + to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by + node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies + to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The + terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements by + node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies + to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by + node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies + to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + nodeSelector: + additionalProperties: + type: string + type: object + podAffinity: + description: Pod affinity is a group of inter pod affinity scheduling + rules. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with + the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Pod anti affinity is a group of inter pod anti affinity + scheduling rules. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with + the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + topologySpreadConstraints: + items: + description: TopologySpreadConstraint specifies how to spread matching + pods among the given topology. + properties: + labelSelector: + description: |- + LabelSelector is used to find matching pods. + Pods that match this label selector are counted to determine the number of pods + in their corresponding topology domain. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select the pods over which + spreading will be calculated. The keys are used to lookup values from the + incoming pod labels, those key-value labels are ANDed with labelSelector + to select the group of existing pods over which spreading will be calculated + for the incoming pod. The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. + MatchLabelKeys cannot be set when LabelSelector isn't set. + Keys that don't exist in the incoming pod labels will + be ignored. A null or empty list means only match against labelSelector. + + This is a beta field and requires the MatchLabelKeysInPodTopologySpread feature gate to be enabled (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + description: |- + MaxSkew describes the degree to which pods may be unevenly distributed. + When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference + between the number of matching pods in the target topology and the global minimum. + The global minimum is the minimum number of matching pods in an eligible domain + or zero if the number of eligible domains is less than MinDomains. + For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same + labelSelector spread as 2/2/1: + In this case, the global minimum is 1. + | zone1 | zone2 | zone3 | + | P P | P P | P | + - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2; + scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2) + violate MaxSkew(1). + - if MaxSkew is 2, incoming pod can be scheduled onto any zone. + When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence + to topologies that satisfy it. + It's a required field. Default value is 1 and 0 is not allowed. + format: int32 + type: integer + minDomains: + description: |- + MinDomains indicates a minimum number of eligible domains. + When the number of eligible domains with matching topology keys is less than minDomains, + Pod Topology Spread treats "global minimum" as 0, and then the calculation of Skew is performed. + And when the number of eligible domains with matching topology keys equals or greater than minDomains, + this value has no effect on scheduling. + As a result, when the number of eligible domains is less than minDomains, + scheduler won't schedule more than maxSkew Pods to those domains. + If value is nil, the constraint behaves as if MinDomains is equal to 1. + Valid values are integers greater than 0. + When value is not nil, WhenUnsatisfiable must be DoNotSchedule. + + For example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains is set to 5 and pods with the same + labelSelector spread as 2/2/2: + | zone1 | zone2 | zone3 | + | P P | P P | P P | + The number of domains is less than 5(MinDomains), so "global minimum" is treated as 0. + In this situation, new pod with the same labelSelector cannot be scheduled, + because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones, + it will violate MaxSkew. + format: int32 + type: integer + nodeAffinityPolicy: + description: |- + NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector + when calculating pod topology spread skew. Options are: + - Honor: only nodes matching nodeAffinity/nodeSelector are included in the calculations. + - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations. + + If this value is nil, the behavior is equivalent to the Honor policy. + type: string + nodeTaintsPolicy: + description: |- + NodeTaintsPolicy indicates how we will treat node taints when calculating + pod topology spread skew. Options are: + - Honor: nodes without taints, along with tainted nodes for which the incoming pod + has a toleration, are included. + - Ignore: node taints are ignored. All nodes are included. + + If this value is nil, the behavior is equivalent to the Ignore policy. + type: string + topologyKey: + description: |- + TopologyKey is the key of node labels. Nodes that have a label with this key + and identical values are considered to be in the same topology. + We consider each as a "bucket", and try to put balanced number + of pods into each bucket. + We define a domain as a particular instance of a topology. + Also, we define an eligible domain as a domain whose nodes meet the requirements of + nodeAffinityPolicy and nodeTaintsPolicy. + e.g. If TopologyKey is "kubernetes.io/hostname", each Node is a domain of that topology. + And, if TopologyKey is "topology.kubernetes.io/zone", each zone is a domain of that topology. + It's a required field. + type: string + whenUnsatisfiable: + description: |- + WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy + the spread constraint. + - DoNotSchedule (default) tells the scheduler not to schedule it. + - ScheduleAnyway tells the scheduler to schedule the pod in any location, + but giving higher precedence to topologies that would help reduce the + skew. + A constraint is considered "Unsatisfiable" for an incoming pod + if and only if every possible node assignment for that pod would violate + "MaxSkew" on some topology. + For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same + labelSelector spread as 3/1/1: + | zone1 | zone2 | zone3 | + | P P P | P | P | + If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled + to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies + MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler + won't make it *more* imbalanced. + It's a required field. + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + type: object + type: object + served: true + storage: true diff --git a/packages/system/cozystack-scheduler/charts/cozystack-scheduler/templates/clusterrole.yaml b/packages/system/cozystack-scheduler/charts/cozystack-scheduler/templates/clusterrole.yaml new file mode 100644 index 00000000..22f196a5 --- /dev/null +++ b/packages/system/cozystack-scheduler/charts/cozystack-scheduler/templates/clusterrole.yaml @@ -0,0 +1,9 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: cozystack-scheduler +rules: + - apiGroups: ["cozystack.io"] + resources: + - schedulingclasses + verbs: ["get", "list", "watch"] diff --git a/packages/system/cozystack-scheduler/charts/cozystack-scheduler/templates/clusterrolebinding.yaml b/packages/system/cozystack-scheduler/charts/cozystack-scheduler/templates/clusterrolebinding.yaml new file mode 100644 index 00000000..00fcd968 --- /dev/null +++ b/packages/system/cozystack-scheduler/charts/cozystack-scheduler/templates/clusterrolebinding.yaml @@ -0,0 +1,38 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: cozystack-scheduler:kube-scheduler +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:kube-scheduler +subjects: + - kind: ServiceAccount + name: cozystack-scheduler + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: cozystack-scheduler:volume-scheduler +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:volume-scheduler +subjects: + - kind: ServiceAccount + name: cozystack-scheduler + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: cozystack-scheduler +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cozystack-scheduler +subjects: + - kind: ServiceAccount + name: cozystack-scheduler + namespace: {{ .Release.Namespace }} diff --git a/packages/system/cozystack-scheduler/charts/cozystack-scheduler/templates/configmap.yaml b/packages/system/cozystack-scheduler/charts/cozystack-scheduler/templates/configmap.yaml new file mode 100644 index 00000000..24e0bc6f --- /dev/null +++ b/packages/system/cozystack-scheduler/charts/cozystack-scheduler/templates/configmap.yaml @@ -0,0 +1,58 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: cozystack-scheduler-config + namespace: {{ .Release.Namespace }} +data: + scheduler-config.yaml: | + apiVersion: kubescheduler.config.k8s.io/v1 + kind: KubeSchedulerConfiguration + leaderElection: + leaderElect: true + resourceNamespace: {{ .Release.Namespace }} + resourceName: cozystack-scheduler + profiles: + - schedulerName: cozystack-scheduler + plugins: + preFilter: + disabled: + - name: InterPodAffinity + - name: NodeAffinity + - name: PodTopologySpread + enabled: + - name: CozystackInterPodAffinity + - name: CozystackNodeAffinity + - name: CozystackPodTopologySpread + - name: CozystackSchedulingClass + filter: + disabled: + - name: InterPodAffinity + - name: NodeAffinity + - name: PodTopologySpread + enabled: + - name: CozystackInterPodAffinity + - name: CozystackNodeAffinity + - name: CozystackPodTopologySpread + - name: CozystackSchedulingClass + preScore: + disabled: + - name: InterPodAffinity + - name: NodeAffinity + - name: PodTopologySpread + enabled: + - name: CozystackInterPodAffinity + - name: CozystackNodeAffinity + - name: CozystackPodTopologySpread + score: + disabled: + - name: InterPodAffinity + - name: NodeAffinity + - name: PodTopologySpread + enabled: + - name: CozystackInterPodAffinity + - name: CozystackNodeAffinity + - name: CozystackPodTopologySpread + {{- with .Values.extenders }} + extenders: + {{- toYaml . | nindent 6 }} + {{- end }} diff --git a/packages/system/cozystack-scheduler/charts/cozystack-scheduler/templates/deployment.yaml b/packages/system/cozystack-scheduler/charts/cozystack-scheduler/templates/deployment.yaml new file mode 100644 index 00000000..0365f0de --- /dev/null +++ b/packages/system/cozystack-scheduler/charts/cozystack-scheduler/templates/deployment.yaml @@ -0,0 +1,40 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: cozystack-scheduler + namespace: {{ .Release.Namespace }} +spec: + replicas: {{ .Values.replicas }} + selector: + matchLabels: + app: cozystack-scheduler + template: + metadata: + labels: + app: cozystack-scheduler + spec: + serviceAccountName: cozystack-scheduler + containers: + - name: cozystack-scheduler + image: {{ .Values.image }} + command: + - /cozystack-scheduler + - --config=/etc/kubernetes/scheduler-config.yaml + {{- with .Values.defaultLabelSelectorKeys }} + - --default-label-selector-keys={{ join "," . }} + {{- end }} + livenessProbe: + httpGet: + path: /healthz + port: 10259 + scheme: HTTPS + initialDelaySeconds: 15 + volumeMounts: + - name: config + mountPath: /etc/kubernetes/scheduler-config.yaml + subPath: scheduler-config.yaml + readOnly: true + volumes: + - name: config + configMap: + name: cozystack-scheduler-config diff --git a/packages/system/cozystack-scheduler/charts/cozystack-scheduler/templates/rolebinding.yaml b/packages/system/cozystack-scheduler/charts/cozystack-scheduler/templates/rolebinding.yaml new file mode 100644 index 00000000..796bf55c --- /dev/null +++ b/packages/system/cozystack-scheduler/charts/cozystack-scheduler/templates/rolebinding.yaml @@ -0,0 +1,40 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: cozystack-scheduler:extension-apiserver-authentication-reader + namespace: kube-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: extension-apiserver-authentication-reader +subjects: + - kind: ServiceAccount + name: cozystack-scheduler + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: cozystack-scheduler:leader-election + namespace: {{ .Release.Namespace }} +rules: + - apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["create", "get", "list", "update", "watch"] + - apiGroups: ["coordination.k8s.io"] + resources: ["leasecandidates"] + verbs: ["create", "get", "list", "update", "watch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: cozystack-scheduler:leader-election + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: cozystack-scheduler:leader-election +subjects: + - kind: ServiceAccount + name: cozystack-scheduler + namespace: {{ .Release.Namespace }} diff --git a/packages/system/cozystack-scheduler/charts/cozystack-scheduler/templates/serviceaccount.yaml b/packages/system/cozystack-scheduler/charts/cozystack-scheduler/templates/serviceaccount.yaml new file mode 100644 index 00000000..876e5f01 --- /dev/null +++ b/packages/system/cozystack-scheduler/charts/cozystack-scheduler/templates/serviceaccount.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: cozystack-scheduler + namespace: {{ .Release.Namespace }} diff --git a/packages/system/cozystack-scheduler/charts/cozystack-scheduler/values.yaml b/packages/system/cozystack-scheduler/charts/cozystack-scheduler/values.yaml new file mode 100644 index 00000000..55b6faff --- /dev/null +++ b/packages/system/cozystack-scheduler/charts/cozystack-scheduler/values.yaml @@ -0,0 +1,23 @@ +image: ghcr.io/cozystack/cozystack/cozystack-scheduler:v0.3.0@sha256:89c285c5c5fe3ed8d7d597acf32fc9f045394e0f8efafd1878d6080cf112f6c2 +replicas: 1 +# defaultLabelSelectorKeys overrides the pod label keys used to auto-populate +# nil LabelSelectors in SchedulingClass affinity and topology spread terms. +# When empty or unset, the binary defaults are used: +# - apps.cozystack.io/application.group +# - apps.cozystack.io/application.kind +# - apps.cozystack.io/application.name +# defaultLabelSelectorKeys: [] + +# extenders is a list of scheduler extenders to call during the scheduling cycle. +# Each entry is passed directly into KubeSchedulerConfiguration.extenders[]. +# Example: +# extenders: +# - urlPrefix: http://linstor-scheduler-extender.cozy-linstor.svc:8099 +# filterVerb: filter +# prioritizeVerb: prioritize +# weight: 5 +# enableHTTPS: false +# httpTimeout: 10s +# nodeCacheCapable: false +# ignorable: true +extenders: [] diff --git a/packages/system/cozystack-scheduler/templates/tenant-clusterroles.yaml b/packages/system/cozystack-scheduler/templates/tenant-clusterroles.yaml new file mode 100644 index 00000000..a7b8fd21 --- /dev/null +++ b/packages/system/cozystack-scheduler/templates/tenant-clusterroles.yaml @@ -0,0 +1,18 @@ +--- +# == schedulingclass view cluster role == +# Aggregated into cozy-tenant-view (and consequently use, admin, super-admin) +# Provides read-only access to SchedulingClass resources +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: cozy:schedulingclass:view + labels: + rbac.cozystack.io/aggregate-to-tenant-view: "true" +rules: +- apiGroups: ["cozystack.io"] + resources: + - schedulingclasses + verbs: + - get + - list + - watch diff --git a/packages/system/cozystack-scheduler/tests/configmap_test.yaml b/packages/system/cozystack-scheduler/tests/configmap_test.yaml new file mode 100644 index 00000000..ce8f372d --- /dev/null +++ b/packages/system/cozystack-scheduler/tests/configmap_test.yaml @@ -0,0 +1,26 @@ +suite: scheduler configmap tests + +templates: + - charts/cozy-cozystack-scheduler/templates/configmap.yaml + +tests: + - it: renders extenders when configured + values: + - ../values-linstor.yaml + asserts: + - isKind: + of: ConfigMap + - matchRegex: + path: data["scheduler-config.yaml"] + pattern: "urlPrefix: http://linstor-scheduler-extender" + - matchRegex: + path: data["scheduler-config.yaml"] + pattern: "ignorable: true" + + - it: omits extenders by default + asserts: + - isKind: + of: ConfigMap + - notMatchRegex: + path: data["scheduler-config.yaml"] + pattern: "extenders:" diff --git a/packages/system/cozystack-scheduler/values-linstor.yaml b/packages/system/cozystack-scheduler/values-linstor.yaml new file mode 100644 index 00000000..ab16ca57 --- /dev/null +++ b/packages/system/cozystack-scheduler/values-linstor.yaml @@ -0,0 +1,10 @@ +cozy-cozystack-scheduler: + extenders: + - urlPrefix: http://linstor-scheduler-extender.cozy-linstor.svc:8099 + filterVerb: filter + prioritizeVerb: prioritize + weight: 5 + enableHTTPS: false + httpTimeout: 10s + nodeCacheCapable: false + ignorable: true diff --git a/packages/system/dashboard/images/openapi-ui/Dockerfile b/packages/system/dashboard/images/openapi-ui/Dockerfile index 4b80c960..82cc6db0 100644 --- a/packages/system/dashboard/images/openapi-ui/Dockerfile +++ b/packages/system/dashboard/images/openapi-ui/Dockerfile @@ -6,7 +6,7 @@ FROM node:${NODE_VERSION}-alpine AS openapi-k8s-toolkit-builder RUN apk add git WORKDIR /src # release/1.4.0 -ARG COMMIT=c67029cc7b7495c65ee0406033576e773a73bb01 +ARG COMMIT=d6b9e4ad0d1eb9d3730f7f0c664792c8dda3214d RUN wget -O- https://github.com/PRO-Robotech/openapi-k8s-toolkit/archive/${COMMIT}.tar.gz | tar -xzvf- --strip-components=1 COPY openapi-k8s-toolkit/patches /patches diff --git a/packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/flatmap-unresolved-placeholder.diff b/packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/flatmap-unresolved-placeholder.diff new file mode 100644 index 00000000..e98861a3 --- /dev/null +++ b/packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/flatmap-unresolved-placeholder.diff @@ -0,0 +1,17 @@ +diff --git a/src/components/molecules/EnrichedTable/organisms/EnrichedTableProvider/utils.ts b/src/components/molecules/EnrichedTable/organisms/EnrichedTableProvider/utils.ts +--- a/src/components/molecules/EnrichedTable/organisms/EnrichedTableProvider/utils.ts ++++ b/src/components/molecules/EnrichedTable/organisms/EnrichedTableProvider/utils.ts +@@ -185,6 +185,11 @@ + return `['${escaped}']` + }) + } +- const jpQueryResult = jp.query(el, `$${resolvedJsonPath}`) +- fieldValue = Array.isArray(jpQueryResult) && jpQueryResult.length === 1 ? jpQueryResult[0] : jpQueryResult ++ if (/_flatMap[^\]]+_Key/.test(resolvedJsonPath)) { ++ // Placeholder was not resolved (row not yet expanded or key missing) — skip query ++ fieldValue = null ++ } else { ++ const jpQueryResult = jp.query(el, `$${resolvedJsonPath}`) ++ fieldValue = Array.isArray(jpQueryResult) && jpQueryResult.length === 1 ? jpQueryResult[0] : jpQueryResult ++ } + } diff --git a/packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/formlistinput-allow-empty.diff b/packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/formlistinput-allow-empty.diff new file mode 100644 index 00000000..1c83df7c --- /dev/null +++ b/packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/formlistinput-allow-empty.diff @@ -0,0 +1,37 @@ +diff --git a/src/localTypes/formExtensions.ts b/src/localTypes/formExtensions.ts +--- a/src/localTypes/formExtensions.ts ++++ b/src/localTypes/formExtensions.ts +@@ -59,2 +59,4 @@ + relatedValuePath?: string ++ allowEmpty?: boolean ++ persistType?: 'str' | 'number' | 'arr' | 'obj' + } +diff --git a/src/components/molecules/BlackholeForm/molecules/FormListInput/FormListInput.tsx b/src/components/molecules/BlackholeForm/molecules/FormListInput/FormListInput.tsx +--- a/src/components/molecules/BlackholeForm/molecules/FormListInput/FormListInput.tsx ++++ b/src/components/molecules/BlackholeForm/molecules/FormListInput/FormListInput.tsx +@@ -149,3 +149,10 @@ + }, [relatedPath, form, arrName, fixedName, relatedFieldValue, onValuesChangeCallBack, isTouchedPeristed]) + ++ // When allowEmpty is set, auto-persist the field so the BFF preserves empty values ++ useEffect(() => { ++ if (customProps.allowEmpty) { ++ persistedControls.onPersistMark(persistName || name, customProps.persistType ?? 'str') ++ } ++ }, [customProps.allowEmpty, customProps.persistType, persistedControls, persistName, name]) ++ + const uri = prepareTemplate({ +@@ -267,5 +274,14 @@ + validateTrigger="onBlur" + hasFeedback={designNewLayout ? { icons: feedbackIcons } : true} + style={{ flex: 1 }} ++ normalize={(value: unknown) => { ++ if (customProps.allowEmpty && (value === undefined || value === null)) { ++ if (customProps.persistType === 'number') return 0 ++ if (customProps.persistType === 'arr') return [] ++ if (customProps.persistType === 'obj') return {} ++ return '' ++ } ++ return value ++ }} + > +