Compare commits

..

2 commits

Author SHA1 Message Date
Andrei Kvapil
a8d32a4bc3
fix: add default values for _cluster config in cozystack-values Secret
Ensure all required keys exist in the _cluster config by merging
ConfigMap data with default values. This prevents errors when accessing
keys that may not be present in the cozystack ConfigMap.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2026-01-04 10:00:17 +01:00
Andrei Kvapil
a5d3757c36
refactor: replace Helm lookup with valuesFrom mechanism
Replace Helm lookup functions with FluxCD valuesFrom for passing
configuration to HelmReleases. This eliminates the need for force
reconcile controllers and provides cleaner config propagation.

Changes:
- Add Secret cozystack-values creation in platform and tenant charts
- Modify cozystack-api to add valuesFrom, filter _ prefixed keys
- Add valuesFrom validation in cozystack-controller
- Add helper templates in cozy-lib for _cluster/_namespace access
- Replace ConfigMap/Namespace lookups in 40+ Helm charts
- Remove CozystackConfigReconciler and TenantHelmReconciler

The Secret cozystack-values contains:
- _cluster: data from cozystack, cozystack-branding, cozystack-scheduling ConfigMaps
- _namespace: service references (etcd, host, ingress, monitoring, seaweedfs)

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2026-01-04 10:00:17 +01:00
1802 changed files with 102002 additions and 201045 deletions

View file

@ -1,5 +0,0 @@
reviews:
auto_review:
enabled: true
auto_incremental_review: true
drafts: false

View file

@ -1,23 +0,0 @@
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

View file

@ -1,110 +0,0 @@
# 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/<name>/patches/` applied by the Makefile.
- For source-code changes in images: suggest a patch in `packages/<name>/images/<name>/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 `<package-name>` 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/`.

2
.github/CODEOWNERS vendored
View file

@ -1 +1 @@
* @kvaps @lllamnyp @lexfrei @androndo @IvanHunters @sircthulhu @myasnikovdaniil
* @kvaps @lllamnyp @nbykov0

View file

@ -1,7 +1,7 @@
---
name: Bug report
about: Create a report to help us improve
labels: 'kind/bug'
labels: 'bug'
assignees: ''
---

View file

@ -1,11 +1,8 @@
<!-- Thank you for making a contribution! Here are some tips for you:
- Use Conventional Commits for the PR title: `type(scope): description`
- Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore
- Scopes are not an exhaustive list — pick the most specific scope for the change and extend the list when a genuinely new area appears. Examples:
- System components: dashboard, platform, operator, cilium, kube-ovn, linstor, fluxcd, cluster-api
- Managed apps: postgres, mariadb, redis, kafka, clickhouse, virtual-machine, kubernetes
- Development and maintenance: api, hack, tests, ci, docs, maintenance
- Breaking changes: append `!` after type/scope (`feat(api)!: ...`) or add a `BREAKING CHANGE:` footer
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium], [kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres], [virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats, even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported to a previous version.
@ -14,19 +11,14 @@
## What this PR does
### Screenshots
<!-- REQUIRED for UI changes: attach screenshots or screen recordings demonstrating
the visual impact of your changes. PRs with UI changes without screenshots will not be merged. -->
### Release note
<!-- Write a release note:
- Explain what has changed internally and for users.
- Start with the same `type(scope):` prefix as in the PR title
- Start with the same [label] as in the PR title
- Follow the guidelines at https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->
```release-note
[]
```

371
.github/labels.yml vendored
View file

@ -1,371 +0,0 @@
# 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

View file

@ -20,14 +20,6 @@ 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:
@ -36,27 +28,27 @@ jobs:
- name: Configure git
env:
APP_TOKEN: ${{ steps.app-token.outputs.token }}
GH_PAT: ${{ secrets.GH_PAT }}
run: |
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 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 --unset-all http.https://github.com/.extraheader || true
- name: Process release branches
uses: actions/github-script@v7
env:
APP_TOKEN: ${{ steps.app-token.outputs.token }}
GH_PAT: ${{ secrets.GH_PAT }}
with:
github-token: ${{ steps.app-token.outputs.token }}
github-token: ${{ secrets.GH_PAT }}
script: |
const { execSync } = require('child_process');
// 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)
// 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)
execSync('git config --unset-all http.https://github.com/.extraheader || true', { encoding: 'utf8' });
// Get all release-X.Y branches

View file

@ -1,61 +0,0 @@
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

View file

@ -1,84 +0,0 @@
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

View file

@ -1,206 +0,0 @@
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(', ')}`);

View file

@ -28,7 +28,7 @@ jobs:
- name: Install generate
run: |
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
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
- name: Run pre-commit hooks
run: |

View file

@ -23,14 +23,6 @@ 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
@ -55,11 +47,11 @@ jobs:
- name: Create tag on merge commit
env:
APP_TOKEN: ${{ steps.app-token.outputs.token }}
GH_PAT: ${{ secrets.GH_PAT }}
run: |
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 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 tag -f ${{ steps.get_tag.outputs.tag }} ${{ github.sha }}
git push -f origin ${{ steps.get_tag.outputs.tag }}
@ -67,7 +59,7 @@ jobs:
- name: Ensure maintenance branch release-X.Y
uses: actions/github-script@v7
with:
github-token: ${{ steps.app-token.outputs.token }}
github-token: ${{ secrets.GH_PAT }}
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\.]+)?$/);
@ -118,95 +110,67 @@ jobs:
}
}
# Publish draft release and ensure correct latest flag
- name: Publish draft release
# Get the latest published release
- name: Get the latest published release
id: latest_release
uses: actions/github-script@v7
with:
script: |
const tag = '${{ steps.get_tag.outputs.tag }}';
try {
const rel = await github.rest.repos.getLatestRelease({
owner: context.repo.owner,
repo: context.repo.repo
});
core.setOutput('tag', rel.data.tag_name);
} catch (_) {
core.setOutput('tag', '');
}
# Compare current tag vs latest using semver-utils
- name: Semver compare
id: semver
uses: madhead/semver-utils@v4.3.0
with:
version: ${{ steps.get_tag.outputs.tag }}
compare-to: ${{ steps.latest_release.outputs.tag }}
# Derive flags: prerelease? make_latest?
- name: Calculate publish flags
id: flags
uses: actions/github-script@v7
with:
script: |
const tag = '${{ steps.get_tag.outputs.tag }}'; // v0.31.5-rc.1
const m = tag.match(/^v(\d+\.\d+\.\d+)(-(?:alpha|beta|rc)\.\d+)?$/);
if (!m) {
core.setFailed(`❌ tag '${tag}' must match 'vX.Y.Z' or 'vX.Y.Z-(alpha|beta|rc).N'`);
return;
}
const isRc = Boolean(m[2]);
const version = m[1] + (m[2] ?? ''); // 0.31.5-rc.1
const isRc = Boolean(m[2]);
core.setOutput('is_rc', isRc);
const outdated = '${{ steps.semver.outputs.comparison-result }}' === '<';
core.setOutput('make_latest', isRc || outdated ? 'false' : 'legacy');
// Parse semver string to comparable numbers
function parseSemver(v) {
const match = v.replace(/^v/, '').match(/^(\d+)\.(\d+)\.(\d+)/);
if (!match) return null;
return {
major: parseInt(match[1]),
minor: parseInt(match[2]),
patch: parseInt(match[3])
};
}
// Compare two semver objects
function compareSemver(a, b) {
if (a.major !== b.major) return a.major - b.major;
if (a.minor !== b.minor) return a.minor - b.minor;
return a.patch - b.patch;
}
const currentSemver = parseSemver(tag);
// Get all releases
# Publish draft release with correct flags
- name: Publish draft release
uses: actions/github-script@v7
with:
script: |
const tag = '${{ steps.get_tag.outputs.tag }}';
const releases = await github.rest.repos.listReleases({
owner: context.repo.owner,
repo: context.repo.repo,
per_page: 100
repo: context.repo.repo
});
// Find draft release to publish
const draft = releases.data.find(r => r.tag_name === tag && r.draft);
if (!draft) throw new Error(`Draft release for ${tag} not found`);
// Find max semver among published releases (excluding current draft)
const publishedReleases = releases.data
.filter(r => !r.draft && !r.prerelease)
.filter(r => /^v\d+\.\d+\.\d+$/.test(r.tag_name))
.map(r => ({ id: r.id, tag: r.tag_name, semver: parseSemver(r.tag_name) }))
.filter(r => r.semver !== null);
let maxRelease = null;
for (const rel of publishedReleases) {
if (!maxRelease || compareSemver(rel.semver, maxRelease.semver) > 0) {
maxRelease = rel;
}
}
// Determine if this release should be latest
const isOutdated = maxRelease && compareSemver(currentSemver, maxRelease.semver) < 0;
const makeLatest = (isRc || isOutdated) ? 'false' : 'true';
if (isRc) {
console.log(`🏷️ ${tag} is a prerelease, make_latest: false`);
} else if (isOutdated) {
console.log(`🏷️ ${tag} < ${maxRelease.tag} (max semver), make_latest: false`);
} else {
console.log(`🏷️ ${tag} is the highest version, make_latest: true`);
}
// Publish the release
await github.rest.repos.updateRelease({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: draft.id,
draft: false,
prerelease: isRc,
make_latest: makeLatest
owner: context.repo.owner,
repo: context.repo.repo,
release_id: draft.id,
draft: false,
prerelease: ${{ steps.flags.outputs.is_rc }},
make_latest: '${{ steps.flags.outputs.make_latest }}'
});
console.log(`🚀 Published release ${tag}`);
// If this is a backport/outdated release, ensure the correct release is marked as latest
if (isOutdated && maxRelease) {
console.log(`🔧 Ensuring ${maxRelease.tag} remains the latest release...`);
await github.rest.repos.updateRelease({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: maxRelease.id,
make_latest: 'true'
});
console.log(`✅ Restored ${maxRelease.tag} as latest release`);
}
console.log(`🚀 Published release for ${tag}`);

View file

@ -6,6 +6,8 @@ env:
on:
pull_request:
types: [opened, synchronize, reopened]
paths-ignore:
- 'docs/**/*'
# Cancel inflight runs for the same PR when a new push arrives.
concurrency:
@ -13,32 +15,16 @@ 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
needs: ["detect-changes"]
# Never run when the PR carries the "release" label or only docs changed.
# Never run when the PR carries the "release" label.
if: |
needs.detect-changes.outputs.code == 'true'
&& !contains(github.event.pull_request.labels.*.name, 'release')
!contains(github.event.pull_request.labels.*.name, 'release')
steps:
- name: Checkout code
@ -85,6 +71,12 @@ jobs:
name: pr-patch
path: _out/assets/pr.patch
- name: Upload installer
uses: actions/upload-artifact@v4
with:
name: cozystack-installer
path: _out/assets/cozystack-installer.yaml
- name: Upload Talos image
uses: actions/upload-artifact@v4
with:
@ -96,17 +88,10 @@ jobs:
runs-on: ubuntu-latest
if: contains(github.event.pull_request.labels.*.name, 'release')
outputs:
disk_id: ${{ steps.fetch_assets.outputs.disk_id }}
installer_id: ${{ steps.fetch_assets.outputs.installer_id }}
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
@ -133,7 +118,7 @@ jobs:
id: fetch_assets
uses: actions/github-script@v7
with:
github-token: ${{ steps.app-token.outputs.token }}
github-token: ${{ secrets.GH_PAT }}
script: |
const tag = '${{ steps.get_tag.outputs.tag }}';
const releases = await github.rest.repos.listReleases({
@ -147,19 +132,19 @@ jobs:
return;
}
const find = (n) => draft.assets.find(a => a.name === n)?.id;
const diskId = find('nocloud-amd64.raw.xz');
if (!diskId) {
const installerId = find('cozystack-installer.yaml');
const diskId = find('nocloud-amd64.raw.xz');
if (!installerId || !diskId) {
core.setFailed('Required assets missing in draft release');
return;
}
core.setOutput('disk_id', diskId);
core.setOutput('installer_id', installerId);
core.setOutput('disk_id', diskId);
e2e:
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
prepare_env:
name: "Prepare environment"
runs-on: [self-hosted]
permissions:
contents: read
packages: read
@ -167,15 +152,6 @@ 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
@ -205,16 +181,16 @@ jobs:
if: contains(github.event.pull_request.labels.*.name, 'release')
run: |
mkdir -p _out/assets
curl -sSL -H "Authorization: token ${APP_TOKEN}" -H "Accept: application/octet-stream" \
curl -sSL -H "Authorization: token ${GH_PAT}" -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:
APP_TOKEN: ${{ steps.app-token.outputs.token }}
GH_PAT: ${{ secrets.GH_PAT }}
- name: Set sandbox ID
run: echo "SANDBOX_NAME=cozy-e2e-sandbox-$(echo "${GITHUB_REPOSITORY}:${GITHUB_WORKFLOW}:${GITHUB_REF}" | sha256sum | cut -c1-10)" >> $GITHUB_ENV
# ▸ Prepare environment
# ▸ Start actual job steps
- name: Prepare workspace
run: |
rm -rf /tmp/$SANDBOX_NAME
@ -234,7 +210,47 @@ jobs:
done
echo "✅ The task completed successfully after $attempt attempts"
# ▸ Install Cozystack
install_cozystack:
name: "Install Cozystack"
runs-on: [self-hosted]
permissions:
contents: read
packages: read
needs: ["prepare_env", "resolve_assets"]
if: ${{ always() && needs.prepare_env.result == 'success' }}
steps:
- name: Prepare _out/assets directory
run: mkdir -p _out/assets
# ▸ Regular PR path download artefacts produced by the *build* job
- name: "Download installer (regular PR)"
if: "!contains(github.event.pull_request.labels.*.name, 'release')"
uses: actions/download-artifact@v4
with:
name: cozystack-installer
path: _out/assets
# ▸ Release PR path fetch artefacts from the corresponding draft release
- name: Download assets from draft release (release PR)
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" \
-o _out/assets/cozystack-installer.yaml \
"https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/assets/${{ needs.resolve_assets.outputs.installer_id }}"
env:
GH_PAT: ${{ secrets.GH_PAT }}
# ▸ Start actual job steps
- name: Set sandbox ID
run: echo "SANDBOX_NAME=cozy-e2e-sandbox-$(echo "${GITHUB_REPOSITORY}:${GITHUB_WORKFLOW}:${GITHUB_REF}" | sha256sum | cut -c1-10)" >> $GITHUB_ENV
- name: Sync _out/assets directory
run: |
mkdir -p /tmp/$SANDBOX_NAME/_out/assets
mv _out/assets/* /tmp/$SANDBOX_NAME/_out/assets/
- name: Install Cozystack into sandbox
run: |
cd /tmp/$SANDBOX_NAME
@ -247,77 +263,107 @@ jobs:
fi
echo "❌ Attempt $attempt failed, retrying..."
done
echo "✅ The task completed successfully after $attempt attempts"
echo "✅ The task completed successfully after $attempt attempts."
- name: Run OpenAPI tests
run: |
cd /tmp/$SANDBOX_NAME
make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME test-openapi
# ▸ Run E2E tests
- name: Run E2E tests
id: e2e_tests
run: |
cd /tmp/$SANDBOX_NAME
failed_tests=""
for app in $(ls hack/e2e-apps/*.bats | xargs -n1 basename | cut -d. -f1); do
echo "::group::Testing $app"
attempt=0
success=false
until [ $attempt -ge 3 ]; do
if make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME test-apps-$app; then
success=true
break
fi
attempt=$((attempt + 1))
echo "❌ Attempt $attempt failed, retrying..."
done
if [ "$success" = true ]; then
echo "✅ Test $app completed successfully"
else
echo "❌ Test $app failed after $attempt attempts"
failed_tests="$failed_tests $app"
fi
echo "::endgroup::"
done
if [ -n "$failed_tests" ]; then
echo "❌ Failed tests:$failed_tests"
exit 1
fi
echo "✅ All E2E tests passed"
detect_test_matrix:
name: "Detect e2e test matrix"
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set.outputs.matrix }}
# ▸ Collect debug information (always runs)
- name: Collect report
if: always()
steps:
- uses: actions/checkout@v4
- id: set
run: |
apps=$(ls hack/e2e-apps/*.bats | cut -f3 -d/ | cut -f1 -d. | jq -R | jq -cs)
echo "matrix={\"app\":$apps}" >> "$GITHUB_OUTPUT"
test_apps:
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.detect_test_matrix.outputs.matrix) }}
name: Test ${{ matrix.app }}
runs-on: [self-hosted]
needs: [install_cozystack,detect_test_matrix]
if: ${{ always() && (needs.install_cozystack.result == 'success' && needs.detect_test_matrix.result == 'success') }}
steps:
- name: Set sandbox ID
run: echo "SANDBOX_NAME=cozy-e2e-sandbox-$(echo "${GITHUB_REPOSITORY}:${GITHUB_WORKFLOW}:${GITHUB_REF}" | sha256sum | cut -c1-10)" >> $GITHUB_ENV
- name: E2E Apps
run: |
cd /tmp/$SANDBOX_NAME
make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME collect-report || true
attempt=0
until make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME test-apps-${{ matrix.app }}; do
attempt=$((attempt + 1))
if [ $attempt -ge 3 ]; then
echo "❌ Attempt $attempt failed, exiting..."
exit 1
fi
echo "❌ Attempt $attempt failed, retrying..."
done
echo "✅ The task completed successfully after $attempt attempts"
collect_debug_information:
name: Collect debug information
runs-on: [self-hosted]
needs: [test_apps]
if: ${{ always() }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set sandbox ID
run: echo "SANDBOX_NAME=cozy-e2e-sandbox-$(echo "${GITHUB_REPOSITORY}:${GITHUB_WORKFLOW}:${GITHUB_REF}" | sha256sum | cut -c1-10)" >> $GITHUB_ENV
- name: Collect report
run: |
cd /tmp/$SANDBOX_NAME
make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME collect-report
- name: Upload cozyreport.tgz
if: always()
uses: actions/upload-artifact@v4
with:
name: cozyreport
path: /tmp/${{ env.SANDBOX_NAME }}/_out/cozyreport.tgz
- name: Collect images list
if: always()
run: |
cd /tmp/$SANDBOX_NAME
make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME collect-images || true
make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME collect-images
- name: Upload image list
if: always()
uses: actions/upload-artifact@v4
with:
name: image-list
path: /tmp/${{ env.SANDBOX_NAME }}/_out/images.txt
# ▸ Tear down environment (always runs)
cleanup:
name: Tear down environment
runs-on: [self-hosted]
needs: [collect_debug_information]
if: ${{ always() && needs.test_apps.result == 'success' }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Set sandbox ID
run: echo "SANDBOX_NAME=cozy-e2e-sandbox-$(echo "${GITHUB_REPOSITORY}:${GITHUB_WORKFLOW}:${GITHUB_REF}" | sha256sum | cut -c1-10)" >> $GITHUB_ENV
- name: Tear down sandbox
if: always()
run: make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME delete || true
run: make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME delete
- name: Remove workspace
if: always()
run: rm -rf /tmp/$SANDBOX_NAME

View file

@ -16,8 +16,6 @@ jobs:
prepare-release:
name: Prepare Release
runs-on: [self-hosted]
outputs:
skip: ${{ steps.check_release.outputs.skip }}
permissions:
contents: write
packages: write
@ -25,14 +23,6 @@ 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
@ -123,29 +113,41 @@ jobs:
- name: Commit release artifacts
if: steps.check_release.outputs.skip == 'false'
env:
APP_TOKEN: ${{ steps.app-token.outputs.token }}
GH_PAT: ${{ secrets.GH_PAT }}
run: |
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 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 --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
# Get `latest_version` from latest published release
- name: Get latest published release
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}"
id: latest_release
uses: actions/github-script@v7
with:
script: |
try {
const rel = await github.rest.repos.getLatestRelease({
owner: context.repo.owner,
repo: context.repo.repo
});
core.setOutput('tag', rel.data.tag_name);
} catch (_) {
core.setOutput('tag', '');
}
# Compare tag (A) with latest (B)
- name: Semver compare
if: steps.check_release.outputs.skip == 'false'
id: semver
uses: madhead/semver-utils@v4.3.0
with:
version: ${{ steps.tag.outputs.tag }} # A
compare-to: ${{ steps.latest_release.outputs.tag }} # B
# Create or reuse draft release
- name: Create / reuse draft release
@ -191,11 +193,11 @@ jobs:
- name: Create release branch
if: steps.check_release.outputs.skip == 'false'
env:
APP_TOKEN: ${{ steps.app-token.outputs.token }}
GH_PAT: ${{ secrets.GH_PAT }}
run: |
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 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}
BRANCH="release-${GITHUB_REF#refs/tags/v}"
git branch -f "$BRANCH"
git push -f origin "$BRANCH"
@ -205,7 +207,7 @@ jobs:
if: steps.check_release.outputs.skip == 'false'
uses: actions/github-script@v7
with:
github-token: ${{ steps.app-token.outputs.token }}
github-token: ${{ secrets.GH_PAT }}
script: |
const version = context.ref.replace('refs/tags/v', '');
const base = '${{ steps.get_base.outputs.branch }}';
@ -223,7 +225,7 @@ jobs:
repo: context.repo.repo,
head,
base,
title: `chore(release): cut v${version}`,
title: `Release v${version}`,
body: `This PR prepares the release \`v${version}\`.`,
draft: false
});
@ -237,312 +239,3 @@ jobs:
} else {
console.log(`PR already exists from ${head} to ${base}`);
}
generate-changelog:
name: Generate Changelog
runs-on: [self-hosted]
needs: [prepare-release]
permissions:
contents: write
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
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('version', version);
core.setOutput('tag', ref);
- name: Checkout main branch
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
fetch-tags: true
token: ${{ steps.app-token.outputs.token }}
- name: Check if changelog already exists
id: check_changelog
run: |
CHANGELOG_FILE="docs/changelogs/v${{ steps.tag.outputs.version }}.md"
if [ -f "$CHANGELOG_FILE" ]; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "Changelog file $CHANGELOG_FILE already exists"
else
echo "exists=false" >> $GITHUB_OUTPUT
echo "Changelog file $CHANGELOG_FILE does not exist"
fi
- name: Setup Node.js
if: steps.check_changelog.outputs.exists == 'false'
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install GitHub Copilot CLI
if: steps.check_changelog.outputs.exists == 'false'
run: npm i -g @github/copilot
- 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: ${{ steps.app-token-read.outputs.token }}
VERSION: ${{ steps.tag.outputs.version }}
run: |
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:
APP_TOKEN: ${{ steps.app-token.outputs.token }}
VERSION: ${{ steps.tag.outputs.version }}
run: |
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: ${{ steps.app-token.outputs.token }}
script: |
const version = '${{ steps.tag.outputs.version }}';
const changelogBranch = `changelog-v${version}`;
const baseBranch = 'main';
// Check if PR already exists
const prs = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
head: `${context.repo.owner}:${changelogBranch}`,
base: baseBranch,
state: 'open'
});
if (prs.data.length > 0) {
const pr = prs.data[0];
console.log(`PR #${pr.number} already exists for changelog branch ${changelogBranch}`);
// Update PR body with latest info
const body = `This PR adds the changelog for release \`v${version}\`.\n\n✅ Changelog has been automatically generated in \`docs/changelogs/v${version}.md\`.`;
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number,
body: body
});
console.log(`Updated existing PR #${pr.number}`);
} else {
// Create new PR
const pr = await github.rest.pulls.create({
owner: context.repo.owner,
repo: context.repo.repo,
head: changelogBranch,
base: baseBranch,
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
});
// Add label if needed
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.data.number,
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

4
.gitignore vendored
View file

@ -80,7 +80,3 @@ fabric.properties
**/.DS_Store
tmp/
# build revision marker (generated by make image-packages)
packages/core/platform/.build-revision
.claude/

View file

@ -1,17 +1,6 @@
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: |

View file

@ -27,12 +27,6 @@ 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)
@ -59,7 +53,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**: Conventional Commits (`type(scope): description`) with `--signoff`
- **Git Commits**: `[component] Description` format with `--signoff`
### What NOT to Do
- ❌ Edit `/vendor/`, `zz_generated.*.go`, upstream charts directly

View file

@ -10,5 +10,3 @@
| 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 |

View file

@ -1,6 +1,4 @@
.PHONY: manifests assets unit-tests helm-unit-tests bats-unit-tests preflight
include hack/common-envs.mk
.PHONY: manifests repos assets unit-tests helm-unit-tests
build-deps:
@command -V find docker skopeo jq gh helm > /dev/null
@ -11,118 +9,50 @@ build-deps:
build: build-deps
make -C packages/apps/http-cache image
make -C packages/apps/mariadb image
make -C packages/apps/mysql image
make -C packages/apps/clickhouse image
make -C packages/apps/kubernetes image
make -C packages/system/monitoring image
make -C packages/extra/monitoring image
make -C packages/system/cozystack-api image
make -C packages/system/cozystack-controller image
make -C packages/system/backup-controller image
make -C packages/system/backupstrategy-controller image
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
make -C packages/core/testing image
make -C packages/core/talos image
make -C packages/core/platform image
make -C packages/core/installer image
make manifests
repos:
rm -rf _out
make -C packages/system repo
make -C packages/apps repo
make -C packages/extra repo
manifests:
mkdir -p _out/assets
cat internal/crdinstall/manifests/*.yaml > _out/assets/cozystack-crds.yaml
# Talos variant (default)
helm template installer packages/core/installer -n cozy-system \
--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 \
--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 \
--show-only templates/cozystack-operator.yaml \
> _out/assets/cozystack-operator-hosted.yaml
(cd packages/core/installer/; helm template -n cozy-installer installer .) > _out/assets/cozystack-installer.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 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:
assets:
make -C packages/core/talos assets
assets-cozypkg: assets-cozypkg-linux-amd64 assets-cozypkg-linux-arm64 assets-cozypkg-darwin-amd64 assets-cozypkg-darwin-arm64 assets-cozypkg-windows-amd64 assets-cozypkg-windows-arm64
(cd _out/assets/ && sha256sum cozypkg-*.tar.gz) > _out/assets/cozypkg-checksums.txt
assets-cozypkg-%:
$(eval EXT := $(if $(filter windows,$(firstword $(subst -, ,$*))),.exe,))
mkdir -p _out/assets
GOOS=$(firstword $(subst -, ,$*)) GOARCH=$(lastword $(subst -, ,$*)) go build -ldflags "-X github.com/cozystack/cozystack/cmd/cozypkg/cmd.Version=v$(COZYSTACK_VERSION)" -o _out/bin/cozypkg-$*/cozypkg$(EXT) ./cmd/cozypkg
cp LICENSE _out/bin/cozypkg-$*/LICENSE
tar -C _out/bin/cozypkg-$* -czf _out/assets/cozypkg-$*.tar.gz LICENSE cozypkg$(EXT)
test:
make -C packages/core/testing apply
make -C packages/core/testing test
unit-tests: helm-unit-tests bats-unit-tests go-unit-tests
unit-tests: helm-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

View file

@ -6,12 +6,11 @@
[![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)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/10177/badge)](https://www.bestpractices.dev/projects/10177)
[![GitHub Commit](https://img.shields.io/github/commit-activity/y/cozystack/cozystack)](https://github.com/cozystack/cozystack/graphs/contributors)
# Cozystack
**Cozystack** is a free platform and framework for building clouds.
**Cozystack** is a free PaaS 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/).

View file

@ -1,102 +0,0 @@
# 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:
<https://github.com/cozystack/cozystack/releases>
## 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: <https://github.com/cozystack/cozystack/releases>
- project changelogs in this repository
- the Cozystack website and documentation: <https://cozystack.io>
## 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.

View file

@ -1,33 +0,0 @@
// 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"`
}

View file

@ -1,88 +0,0 @@
//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
}

View file

@ -1,112 +0,0 @@
// 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:="<password>"
ResticPassword string `json:"resticPassword"`
// Access key for S3 authentication.
// +kubebuilder:default:="<your-access-key>"
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:="<your-secret-key>"
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

View file

@ -1,141 +0,0 @@
//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
}

View file

@ -1,162 +0,0 @@
// 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

View file

@ -1,234 +0,0 @@
//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
}

View file

@ -1,24 +0,0 @@
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
)

View file

@ -1,56 +0,0 @@
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=

View file

@ -1,114 +0,0 @@
// 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

View file

@ -1,186 +0,0 @@
//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
}

View file

@ -1,72 +0,0 @@
// 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 <ip:port>.
// +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

View file

@ -1,123 +0,0 @@
//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
}

View file

@ -1,90 +0,0 @@
// 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

View file

@ -1,142 +0,0 @@
//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
}

View file

@ -1,279 +0,0 @@
// 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 `<cluster-name>.<tenant-host>` 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

View file

@ -1,455 +0,0 @@
//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
}

View file

@ -1,109 +0,0 @@
// 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:="<password>"
ResticPassword string `json:"resticPassword"`
// Access key for S3 authentication.
// +kubebuilder:default:="<your-access-key>"
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:="<your-secret-key>"
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

View file

@ -1,171 +0,0 @@
//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
}

View file

@ -1,149 +0,0 @@
// 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

View file

@ -1,227 +0,0 @@
//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
}

View file

@ -1,78 +0,0 @@
// 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

View file

@ -1,149 +0,0 @@
//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
}

View file

@ -1,51 +0,0 @@
// 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

View file

@ -1,85 +0,0 @@
//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
}

View file

@ -1,115 +0,0 @@
// 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

View file

@ -1,161 +0,0 @@
//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
}

View file

@ -1,153 +0,0 @@
// 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:="<your-access-key>"
S3AccessKey string `json:"s3AccessKey,omitempty"`
// Secret key for S3 authentication.
// +kubebuilder:default:="<your-secret-key>"
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

View file

@ -1,240 +0,0 @@
//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
}

View file

@ -1,48 +0,0 @@
// 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

View file

@ -1,85 +0,0 @@
//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
}

View file

@ -1,77 +0,0 @@
// 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

View file

@ -1,155 +0,0 @@
//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
}

View file

@ -1,57 +0,0 @@
// 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

View file

@ -1,85 +0,0 @@
//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
}

View file

@ -1,75 +0,0 @@
// 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

View file

@ -1,126 +0,0 @@
//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
}

View file

@ -1,41 +0,0 @@
// 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"`
}

View file

@ -1,74 +0,0 @@
//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
}

View file

@ -1,61 +0,0 @@
// 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 {
}

View file

@ -1,163 +0,0 @@
//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
}

View file

@ -1,100 +0,0 @@
// 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

View file

@ -1,160 +0,0 @@
//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
}

View file

@ -1,49 +0,0 @@
// 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"`
}

View file

@ -1,126 +0,0 @@
//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
}

View file

@ -1,56 +0,0 @@
// 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

View file

@ -1,111 +0,0 @@
//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
}

View file

@ -6,7 +6,6 @@
package v1alpha1
import (
velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
@ -48,17 +47,7 @@ type VeleroList struct {
}
// VeleroSpec specifies the desired strategy for backing up with Velero.
type VeleroSpec struct {
Template VeleroTemplate `json:"template"`
}
// VeleroTemplate describes the data a backup.velero.io should have when
// templated from a Velero backup strategy.
type VeleroTemplate struct {
Spec velerov1.BackupSpec `json:"spec"`
// +optional
RestoreSpec *velerov1.RestoreSpec `json:"restoreSpec,omitempty"`
}
type VeleroSpec struct{}
type VeleroStatus struct {
Conditions []metav1.Condition `json:"conditions,omitempty"`

View file

@ -21,7 +21,6 @@ limitations under the License.
package v1alpha1
import (
velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
@ -128,7 +127,7 @@ func (in *Velero) DeepCopyInto(out *Velero) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Spec = in.Spec
in.Status.DeepCopyInto(&out.Status)
}
@ -185,7 +184,6 @@ func (in *VeleroList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VeleroSpec) DeepCopyInto(out *VeleroSpec) {
*out = *in
in.Template.DeepCopyInto(&out.Template)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VeleroSpec.
@ -219,24 +217,3 @@ func (in *VeleroStatus) DeepCopy() *VeleroStatus {
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VeleroTemplate) DeepCopyInto(out *VeleroTemplate) {
*out = *in
in.Spec.DeepCopyInto(&out.Spec)
if in.RestoreSpec != nil {
in, out := &in.RestoreSpec, &out.RestoreSpec
*out = new(velerov1.RestoreSpec)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VeleroTemplate.
func (in *VeleroTemplate) DeepCopy() *VeleroTemplate {
if in == nil {
return nil
}
out := new(VeleroTemplate)
in.DeepCopyInto(out)
return out
}

View file

@ -100,13 +100,13 @@ Describe **when**, **how**, and **where** to back up a specific managed applicat
```go
type PlanSpec struct {
// Application to back up.
// If apiGroup is not specified, it defaults to "apps.cozystack.io".
ApplicationRef corev1.TypedLocalObjectReference `json:"applicationRef"`
// BackupClassName references a BackupClass that contains strategy and other parameters (e.g. storage reference).
// The BackupClass will be resolved to determine the appropriate strategy and parameters
// based on the ApplicationRef.
BackupClassName string `json:"backupClassName"`
// Where backups should be stored.
StorageRef corev1.TypedLocalObjectReference `json:"storageRef"`
// Driver-specific BackupStrategy to use.
StrategyRef corev1.TypedLocalObjectReference `json:"strategyRef"`
// When backups should run.
Schedule PlanSchedule `json:"schedule"`
@ -145,12 +145,12 @@ Core Plan controller:
* Create a `BackupJob` in the same namespace:
* `spec.planRef.name = plan.Name`
* `spec.applicationRef = plan.spec.applicationRef` (normalized with default apiGroup if not specified)
* `spec.backupClassName = plan.spec.backupClassName`
* `spec.applicationRef = plan.spec.applicationRef`
* `spec.storageRef = plan.spec.storageRef`
* `spec.strategyRef = plan.spec.strategyRef`
* `spec.triggeredBy = "Plan"`
* Set `ownerReferences` so the `BackupJob` is owned by the `Plan`.
**Note:** The `BackupJob` controller resolves the `BackupClass` to determine the appropriate strategy and parameters, based on the `ApplicationRef`. The strategy template is processed with a context containing the `Application` object and `Parameters` from the `BackupClass`.
The Plan controller does **not**:
* Execute backups itself.
@ -159,64 +159,17 @@ The Plan controller does **not**:
---
### 4.2 BackupClass
### 4.2 Storage
**Group/Kind**
`backups.cozystack.io/v1alpha1, Kind=BackupClass`
**API Shape**
**Purpose**
Define a class of backup configurations that encapsulate strategy and parameters per application type. `BackupClass` is a cluster-scoped resource that allows admins to configure backup strategies and parameters in a reusable way.
TBD
**Key fields (spec)**
**Storage usage**
```go
type BackupClassSpec struct {
// Strategies is a list of backup strategies, each matching a specific application type.
Strategies []BackupClassStrategy `json:"strategies"`
}
type BackupClassStrategy struct {
// StrategyRef references the driver-specific BackupStrategy (e.g., Velero).
StrategyRef corev1.TypedLocalObjectReference `json:"strategyRef"`
// Application specifies which application types this strategy applies to.
// If apiGroup is not specified, it defaults to "apps.cozystack.io".
Application ApplicationSelector `json:"application"`
// Parameters holds strategy-specific parameters, like storage reference.
// Common parameters include:
// - backupStorageLocationName: Name of Velero BackupStorageLocation
// +optional
Parameters map[string]string `json:"parameters,omitempty"`
}
type ApplicationSelector struct {
// APIGroup is the API group of the application.
// If not specified, defaults to "apps.cozystack.io".
// +optional
APIGroup *string `json:"apiGroup,omitempty"`
// Kind is the kind of the application (e.g., VirtualMachine, MariaDB).
Kind string `json:"kind"`
}
```
**BackupClass resolution**
* When a `BackupJob` or `Plan` references a `BackupClass` via `backupClassName`, the controller:
1. Fetches the `BackupClass` by name.
2. Matches the `ApplicationRef` against strategies in the `BackupClass`:
* Normalizes `ApplicationRef.apiGroup` (defaults to `"apps.cozystack.io"` if not specified).
* Finds a strategy where `ApplicationSelector` matches the `ApplicationRef` (apiGroup and kind).
3. Returns the matched `StrategyRef` and `Parameters`.
* Strategy templates (e.g., Velero's `backupTemplate.spec`) are processed with a context containing:
* `Application`: The application object being backed up.
* `Parameters`: The parameters from the matched `BackupClassStrategy`.
**Parameters**
* Parameters are passed via `Parameters` in the `BackupClass` (e.g., `backupStorageLocationName` for Velero).
* The driver uses these parameters to resolve the actual resources (e.g., Velero's `BackupStorageLocation` CRD).
* `Plan` and `BackupJob` reference `Storage` via `TypedLocalObjectReference`.
* Drivers read `Storage` to know how/where to store or read artifacts.
* Core treats `Storage` spec as opaque; it does not directly talk to S3 or buckets.
---
@ -236,13 +189,16 @@ type BackupJobSpec struct {
PlanRef *corev1.LocalObjectReference `json:"planRef,omitempty"`
// Application to back up.
// If apiGroup is not specified, it defaults to "apps.cozystack.io".
ApplicationRef corev1.TypedLocalObjectReference `json:"applicationRef"`
// BackupClassName references a BackupClass that contains strategy and related parameters
// The BackupClass will be resolved to determine the appropriate strategy and parameters
// based on the ApplicationRef.
BackupClassName string `json:"backupClassName"`
// Storage to use.
StorageRef corev1.TypedLocalObjectReference `json:"storageRef"`
// Driver-specific BackupStrategy to use.
StrategyRef corev1.TypedLocalObjectReference `json:"strategyRef"`
// Informational: what triggered this run ("Plan", "Manual", etc.).
TriggeredBy string `json:"triggeredBy,omitempty"`
}
```
@ -267,9 +223,7 @@ type BackupJobStatus struct {
* Each driver controller:
* Watches `BackupJob`.
* Resolves the `BackupClass` referenced by `spec.backupClassName`.
* Matches the `ApplicationRef` against strategies in the `BackupClass` to find the appropriate strategy.
* Reconciles runs where the resolved strategy's `apiGroup/kind` matches its **strategy type(s)**.
* Reconciles runs where `spec.strategyRef.apiGroup/kind` matches its **strategy type(s)**.
* Driver responsibilities:
1. On first reconcile:
@ -278,12 +232,7 @@ type BackupJobStatus struct {
* Set `status.phase = Running`.
2. Resolve inputs:
* Resolve `BackupClass` from `spec.backupClassName`.
* Match `ApplicationRef` against `BackupClass` strategies to get `StrategyRef` and `Parameters`.
* Read `Strategy` (driver-owned CRD) from `StrategyRef`.
* Read `Application` from `ApplicationRef`.
* Extract parameters from `Parameters` (e.g., `backupStorageLocationName` for Velero).
* Process strategy template with context: `Application` object and `Parameters` from `BackupClass`.
* Read `Strategy` (driver-owned CRD), `Storage`, `Application`, optionally `Plan`.
3. Execute backup logic (implementation-specific).
4. On success:
@ -315,14 +264,13 @@ Represent a single **backup artifact** for a given application, decoupled from a
type BackupSpec struct {
ApplicationRef corev1.TypedLocalObjectReference `json:"applicationRef"`
PlanRef *corev1.LocalObjectReference `json:"planRef,omitempty"`
StorageRef corev1.TypedLocalObjectReference `json:"storageRef"`
StrategyRef corev1.TypedLocalObjectReference `json:"strategyRef"`
TakenAt metav1.Time `json:"takenAt"`
DriverMetadata map[string]string `json:"driverMetadata,omitempty"`
}
```
**Note:** Parameters are not stored directly in `Backup`. Instead, they are resolved from `BackupClass` parameters when the backup was created. The storage location is managed by the driver (e.g., Velero's `BackupStorageLocation`) and referenced via parameters in the `BackupClass`.
**Key fields (status)**
```go
@ -342,8 +290,7 @@ type BackupStatus struct {
* Creates a `Backup` in the same namespace (typically owned by the `BackupJob`).
* Populates `spec` fields with:
* The application reference.
* The strategy reference (resolved from `BackupClass` during `BackupJob` execution).
* The application, storage, strategy references.
* `takenAt`.
* Optional `driverMetadata`.
* Sets `status` with:
@ -359,8 +306,6 @@ type BackupStatus struct {
* Anchor `RestoreJob` operations.
* Implement higher-level policies (retention) if needed.
**Note:** Parameters are resolved from `BackupClass` when the `BackupJob` is created. The driver uses these parameters to determine where to store backups. The storage location itself is managed by the driver (e.g., Velero's `BackupStorageLocation` CRD) and is not directly referenced in the `Backup` resource. When restoring, the driver resolves the storage location from the original `BackupClass` parameters or from the driver's own metadata.
---
### 4.5 RestoreJob
@ -408,13 +353,13 @@ type RestoreJobStatus struct {
* Determines effective:
* **Strategy**: `backup.spec.strategyRef`.
* **Storage**: Resolved from driver metadata or `BackupClass` parameters (e.g., `backupStorageLocationName` stored in `driverMetadata` or resolved from the original `BackupClass`).
* **Storage**: `backup.spec.storageRef`.
* **Target application**: `spec.targetApplicationRef` or `backup.spec.applicationRef`.
* If effective strategys GVK is one of its supported strategy types → driver is responsible.
3. Behaviour:
* On first reconcile, set `status.startedAt` and `phase = Running`.
* Resolve `Backup`, storage location (from driver metadata or `BackupClass`), `Strategy`, target application.
* Resolve `Backup`, `Storage`, `Strategy`, target application.
* Execute restore logic (implementation-specific).
* On success:
@ -469,10 +414,8 @@ The Cozystack backups core API:
* Uses a single group, `backups.cozystack.io`, for all core CRDs.
* Cleanly separates:
* **When** (Plan schedule) core-owned.
* **How & where** (BackupClass) central configuration unit that encapsulates strategy and parameters (e.g., storage reference) per application type, resolved per BackupJob/Plan.
* **Execution** (BackupJob) created by Plan when schedule fires, resolves BackupClass to get strategy and parameters, then delegates to driver.
* **When & where** (Plan + Storage) core-owned.
* **What backup artifacts exist** (Backup) driver-created but cluster-visible.
* **Restore lifecycle** (RestoreJob) shared contract boundary.
* **Execution lifecycle** (BackupJob, RestoreJob) shared contract boundary.
* Allows multiple strategy drivers to implement backup/restore logic without entangling their implementation with the core API.

View file

@ -57,6 +57,10 @@ type BackupSpec struct {
// +optional
PlanRef *corev1.LocalObjectReference `json:"planRef,omitempty"`
// StorageRef refers to the Storage object that describes where the backup
// artifact is stored.
StorageRef corev1.TypedLocalObjectReference `json:"storageRef"`
// StrategyRef refers to the driver-specific BackupStrategy that was used
// to create this backup. This allows the driver to later perform restores.
StrategyRef corev1.TypedLocalObjectReference `json:"strategyRef"`
@ -72,15 +76,6 @@ 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.
@ -92,15 +87,6 @@ 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"`

View file

@ -1,85 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Package v1alpha1 defines backups.cozystack.io API types.
//
// Group: backups.cozystack.io
// Version: v1alpha1
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
func init() {
SchemeBuilder.Register(func(s *runtime.Scheme) error {
s.AddKnownTypes(GroupVersion,
&BackupClass{},
&BackupClassList{},
)
return nil
})
}
// +kubebuilder:object:root=true
// +kubebuilder:resource:scope=Cluster
// +kubebuilder:subresource:status
// BackupClass defines a class of backup configurations that can be referenced
// by BackupJob and Plan resources. It encapsulates strategy and storage configuration
// per application type.
type BackupClass struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec BackupClassSpec `json:"spec,omitempty"`
Status BackupClassStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// BackupClassList contains a list of BackupClasses.
type BackupClassList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []BackupClass `json:"items"`
}
// BackupClassSpec defines the desired state of a BackupClass.
type BackupClassSpec struct {
// Strategies is a list of backup strategies, each matching a specific application type.
Strategies []BackupClassStrategy `json:"strategies"`
}
// BackupClassStrategy defines a backup strategy for a specific application type.
type BackupClassStrategy struct {
// StrategyRef references the driver-specific BackupStrategy (e.g., Velero).
StrategyRef corev1.TypedLocalObjectReference `json:"strategyRef"`
// Application specifies which application types this strategy applies to.
Application ApplicationSelector `json:"application"`
// Parameters holds strategy-specific and storage-specific parameters.
// Common parameters include:
// - backupStorageLocationName: Name of Velero BackupStorageLocation
// +optional
Parameters map[string]string `json:"parameters,omitempty"`
}
// ApplicationSelector specifies which application types a strategy applies to.
type ApplicationSelector struct {
// APIGroup is the API group of the application.
// If not specified, defaults to "apps.cozystack.io".
// +optional
APIGroup *string `json:"apiGroup,omitempty"`
// Kind is the kind of the application (e.g., VirtualMachine, MariaDB).
Kind string `json:"kind"`
}
// BackupClassStatus defines the observed state of a BackupClass.
type BackupClassStatus struct {
// Conditions represents the latest available observations of a BackupClass's state.
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`
}

View file

@ -21,15 +21,6 @@ func init() {
})
}
const (
OwningJobNameLabel = thisGroup + "/owned-by.BackupJobName"
OwningJobNamespaceLabel = thisGroup + "/owned-by.BackupJobNamespace"
// DefaultApplicationAPIGroup is the default API group for applications
// when not specified in ApplicationRef or ApplicationSelector.
DefaultApplicationAPIGroup = "apps.cozystack.io"
)
// BackupJobPhase represents the lifecycle phase of a BackupJob.
type BackupJobPhase string
@ -50,16 +41,15 @@ type BackupJobSpec struct {
// ApplicationRef holds a reference to the managed application whose state
// is being backed up.
// If apiGroup is not specified, it defaults to "apps.cozystack.io".
ApplicationRef corev1.TypedLocalObjectReference `json:"applicationRef"`
// BackupClassName references a BackupClass that contains strategy and storage configuration.
// The BackupClass will be resolved to determine the appropriate strategy and storage
// based on the ApplicationRef.
// This field is immutable once the BackupJob is created.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="backupClassName is immutable"
BackupClassName string `json:"backupClassName"`
// StorageRef holds a reference to the Storage object that describes where
// the backup will be stored.
StorageRef corev1.TypedLocalObjectReference `json:"storageRef"`
// StrategyRef holds a reference to the driver-specific BackupStrategy object
// that describes how the backup should be created.
StrategyRef corev1.TypedLocalObjectReference `json:"strategyRef"`
}
// BackupJobStatus represents the observed state of a BackupJob.
@ -95,8 +85,6 @@ type BackupJobStatus struct {
// The field indexing on applicationRef will be needed later to display per-app backup resources.
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",priority=0
// +kubebuilder:selectablefield:JSONPath=`.spec.applicationRef.apiGroup`
// +kubebuilder:selectablefield:JSONPath=`.spec.applicationRef.kind`
// +kubebuilder:selectablefield:JSONPath=`.spec.applicationRef.name`
@ -119,13 +107,3 @@ type BackupJobList struct {
metav1.ListMeta `json:"metadata,omitempty"`
Items []BackupJob `json:"items"`
}
// NormalizeApplicationRef sets the default apiGroup to DefaultApplicationAPIGroup if it's not specified.
// This function is exported so it can be used by other packages (e.g., controllers, factories).
func NormalizeApplicationRef(ref corev1.TypedLocalObjectReference) corev1.TypedLocalObjectReference {
if ref.APIGroup == nil || *ref.APIGroup == "" {
apiGroup := DefaultApplicationAPIGroup
ref.APIGroup = &apiGroup
}
return ref
}

View file

@ -25,13 +25,8 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
)
const (
thisGroup = "backups.cozystack.io"
thisVersion = "v1alpha1"
)
var (
GroupVersion = schema.GroupVersion{Group: thisGroup, Version: thisVersion}
GroupVersion = schema.GroupVersion{Group: "backups.cozystack.io", Version: "v1alpha1"}
SchemeBuilder = runtime.NewSchemeBuilder(addGroupVersion)
AddToScheme = SchemeBuilder.AddToScheme
)

View file

@ -65,13 +65,15 @@ type PlanList struct {
type PlanSpec struct {
// ApplicationRef holds a reference to the managed application,
// whose state and configuration must be backed up.
// If apiGroup is not specified, it defaults to "apps.cozystack.io".
ApplicationRef corev1.TypedLocalObjectReference `json:"applicationRef"`
// BackupClassName references a BackupClass that contains strategy and storage configuration.
// The BackupClass will be resolved to determine the appropriate strategy and storage
// based on the ApplicationRef.
BackupClassName string `json:"backupClassName"`
// StorageRef holds a reference to the Storage object that
// describes the location where the backup will be stored.
StorageRef corev1.TypedLocalObjectReference `json:"storageRef"`
// StrategyRef holds a reference to the Strategy object that
// describes, how a backup copy is to be created.
StrategyRef corev1.TypedLocalObjectReference `json:"strategyRef"`
// Schedule specifies when backup copies are created.
Schedule PlanSchedule `json:"schedule"`

View file

@ -42,12 +42,6 @@ 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.
@ -77,8 +71,6 @@ type RestoreJobStatus struct {
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",priority=0
// RestoreJob represents a single execution of a restore from a Backup.
type RestoreJob struct {

View file

@ -26,26 +26,6 @@ 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 *ApplicationSelector) DeepCopyInto(out *ApplicationSelector) {
*out = *in
if in.APIGroup != nil {
in, out := &in.APIGroup, &out.APIGroup
*out = new(string)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationSelector.
func (in *ApplicationSelector) DeepCopy() *ApplicationSelector {
if in == nil {
return nil
}
out := new(ApplicationSelector)
in.DeepCopyInto(out)
return out
}
// 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
@ -88,133 +68,6 @@ func (in *BackupArtifact) DeepCopy() *BackupArtifact {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BackupClass) DeepCopyInto(out *BackupClass) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupClass.
func (in *BackupClass) DeepCopy() *BackupClass {
if in == nil {
return nil
}
out := new(BackupClass)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *BackupClass) 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 *BackupClassList) DeepCopyInto(out *BackupClassList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]BackupClass, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupClassList.
func (in *BackupClassList) DeepCopy() *BackupClassList {
if in == nil {
return nil
}
out := new(BackupClassList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *BackupClassList) 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 *BackupClassSpec) DeepCopyInto(out *BackupClassSpec) {
*out = *in
if in.Strategies != nil {
in, out := &in.Strategies, &out.Strategies
*out = make([]BackupClassStrategy, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupClassSpec.
func (in *BackupClassSpec) DeepCopy() *BackupClassSpec {
if in == nil {
return nil
}
out := new(BackupClassSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BackupClassStatus) DeepCopyInto(out *BackupClassStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]metav1.Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupClassStatus.
func (in *BackupClassStatus) DeepCopy() *BackupClassStatus {
if in == nil {
return nil
}
out := new(BackupClassStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BackupClassStrategy) DeepCopyInto(out *BackupClassStrategy) {
*out = *in
in.StrategyRef.DeepCopyInto(&out.StrategyRef)
in.Application.DeepCopyInto(&out.Application)
if in.Parameters != nil {
in, out := &in.Parameters, &out.Parameters
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupClassStrategy.
func (in *BackupClassStrategy) DeepCopy() *BackupClassStrategy {
if in == nil {
return nil
}
out := new(BackupClassStrategy)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BackupJob) DeepCopyInto(out *BackupJob) {
*out = *in
@ -283,6 +136,8 @@ func (in *BackupJobSpec) DeepCopyInto(out *BackupJobSpec) {
**out = **in
}
in.ApplicationRef.DeepCopyInto(&out.ApplicationRef)
in.StorageRef.DeepCopyInto(&out.StorageRef)
in.StrategyRef.DeepCopyInto(&out.StrategyRef)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupJobSpec.
@ -371,6 +226,7 @@ func (in *BackupSpec) DeepCopyInto(out *BackupSpec) {
*out = new(v1.LocalObjectReference)
**out = **in
}
in.StorageRef.DeepCopyInto(&out.StorageRef)
in.StrategyRef.DeepCopyInto(&out.StrategyRef)
in.TakenAt.DeepCopyInto(&out.TakenAt)
if in.DriverMetadata != nil {
@ -400,11 +256,6 @@ 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))
@ -424,21 +275,6 @@ 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
@ -517,6 +353,8 @@ func (in *PlanSchedule) DeepCopy() *PlanSchedule {
func (in *PlanSpec) DeepCopyInto(out *PlanSpec) {
*out = *in
in.ApplicationRef.DeepCopyInto(&out.ApplicationRef)
in.StorageRef.DeepCopyInto(&out.StorageRef)
in.StrategyRef.DeepCopyInto(&out.StrategyRef)
out.Schedule = in.Schedule
}
@ -620,11 +458,6 @@ 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.

View file

@ -253,25 +253,3 @@ type FactoryList struct {
metav1.ListMeta `json:"metadata,omitempty"`
Items []Factory `json:"items"`
}
// -----------------------------------------------------------------------------
// CustomFormsOverrideMapping
// -----------------------------------------------------------------------------
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=cfomappings,scope=Cluster
// +kubebuilder:subresource:status
type CFOMapping struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ArbitrarySpec `json:"spec"`
Status CommonStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
type CFOMappingList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []CFOMapping `json:"items"`
}

View file

@ -69,9 +69,6 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&Factory{},
&FactoryList{},
&CFOMapping{},
&CFOMappingList{},
)
metav1.AddToGroupVersion(scheme, GroupVersion)
return nil

View file

@ -159,65 +159,6 @@ func (in *BreadcrumbList) DeepCopyObject() runtime.Object {
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CFOMapping) DeepCopyInto(out *CFOMapping) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CFOMapping.
func (in *CFOMapping) DeepCopy() *CFOMapping {
if in == nil {
return nil
}
out := new(CFOMapping)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CFOMapping) 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 *CFOMappingList) DeepCopyInto(out *CFOMappingList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]CFOMapping, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CFOMappingList.
func (in *CFOMappingList) DeepCopy() *CFOMappingList {
if in == nil {
return nil
}
out := new(CFOMappingList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CFOMappingList) 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 *CommonStatus) DeepCopyInto(out *CommonStatus) {
*out = *in

View file

@ -17,52 +17,69 @@ limitations under the License.
package v1alpha1
import (
helmv2 "github.com/fluxcd/helm-controller/api/v2"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// +kubebuilder:object:root=true
// +kubebuilder:resource:scope=Cluster
// ApplicationDefinition is the Schema for the applicationdefinitions API
type ApplicationDefinition struct {
// CozystackResourceDefinition is the Schema for the cozystackresourcedefinitions API
type CozystackResourceDefinition struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ApplicationDefinitionSpec `json:"spec,omitempty"`
Spec CozystackResourceDefinitionSpec `json:"spec,omitempty"`
}
// +kubebuilder:object:root=true
// ApplicationDefinitionList contains a list of ApplicationDefinitions
type ApplicationDefinitionList struct {
// CozystackResourceDefinitionList contains a list of CozystackResourceDefinitions
type CozystackResourceDefinitionList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ApplicationDefinition `json:"items"`
Items []CozystackResourceDefinition `json:"items"`
}
func init() {
SchemeBuilder.Register(&ApplicationDefinition{}, &ApplicationDefinitionList{})
SchemeBuilder.Register(&CozystackResourceDefinition{}, &CozystackResourceDefinitionList{})
}
type ApplicationDefinitionSpec struct {
type CozystackResourceDefinitionSpec struct {
// Application configuration
Application ApplicationDefinitionApplication `json:"application"`
Application CozystackResourceDefinitionApplication `json:"application"`
// Release configuration
Release ApplicationDefinitionRelease `json:"release"`
Release CozystackResourceDefinitionRelease `json:"release"`
// Secret selectors
Secrets ApplicationDefinitionResources `json:"secrets,omitempty"`
Secrets CozystackResourceDefinitionResources `json:"secrets,omitempty"`
// Service selectors
Services ApplicationDefinitionResources `json:"services,omitempty"`
Services CozystackResourceDefinitionResources `json:"services,omitempty"`
// Ingress selectors
Ingresses ApplicationDefinitionResources `json:"ingresses,omitempty"`
Ingresses CozystackResourceDefinitionResources `json:"ingresses,omitempty"`
// Dashboard configuration for this resource
Dashboard *ApplicationDefinitionDashboard `json:"dashboard,omitempty"`
Dashboard *CozystackResourceDefinitionDashboard `json:"dashboard,omitempty"`
}
type ApplicationDefinitionApplication struct {
type CozystackResourceDefinitionChart struct {
// Name of the Helm chart
Name string `json:"name"`
// Source reference for the Helm chart
SourceRef SourceRef `json:"sourceRef"`
}
type SourceRef struct {
// Kind of the source reference
// +kubebuilder:default:="HelmRepository"
Kind string `json:"kind"`
// Name of the source reference
Name string `json:"name"`
// Namespace of the source reference
// +kubebuilder:default:="cozy-public"
Namespace string `json:"namespace"`
}
type CozystackResourceDefinitionApplication struct {
// Kind of the application, used for UI and API
Kind string `json:"kind"`
// OpenAPI schema for the application, used for API validation
@ -73,16 +90,17 @@ type ApplicationDefinitionApplication struct {
Singular string `json:"singular"`
}
type ApplicationDefinitionRelease struct {
// Reference to the chart source
ChartRef *helmv2.CrossNamespaceSourceReference `json:"chartRef"`
type CozystackResourceDefinitionRelease struct {
// Helm chart configuration
// +optional
Chart CozystackResourceDefinitionChart `json:"chart,omitempty"`
// Labels for the release
Labels map[string]string `json:"labels,omitempty"`
// Prefix for the release name
Prefix string `json:"prefix"`
}
// ApplicationDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support.
// CozystackResourceDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support.
// A resource matches this selector only if it satisfies ALL criteria:
// - Label selector conditions (matchExpressions and matchLabels)
// - AND has a name that matches one of the names in resourceNames (if specified)
@ -105,7 +123,7 @@ type ApplicationDefinitionRelease struct {
// - "{{ .name }}-secret"
// - "{{ .kind }}-{{ .name }}-tls"
// - "specificname"
type ApplicationDefinitionResourceSelector struct {
type CozystackResourceDefinitionResourceSelector struct {
metav1.LabelSelector `json:",inline"`
// ResourceNames is a list of resource names to match
// If specified, the resource must have one of these exact names to match the selector
@ -113,16 +131,16 @@ type ApplicationDefinitionResourceSelector struct {
ResourceNames []string `json:"resourceNames,omitempty"`
}
type ApplicationDefinitionResources struct {
type CozystackResourceDefinitionResources struct {
// Exclude contains an array of resource selectors that target resources.
// If a resource matches the selector in any of the elements in the array, it is
// hidden from the user, regardless of the matches in the include array.
Exclude []*ApplicationDefinitionResourceSelector `json:"exclude,omitempty"`
Exclude []*CozystackResourceDefinitionResourceSelector `json:"exclude,omitempty"`
// Include contains an array of resource selectors that target resources.
// If a resource matches the selector in any of the elements in the array, and
// matches none of the selectors in the exclude array that resource is marked
// as a tenant resource and is visible to users.
Include []*ApplicationDefinitionResourceSelector `json:"include,omitempty"`
Include []*CozystackResourceDefinitionResourceSelector `json:"include,omitempty"`
}
// ---- Dashboard types ----
@ -139,8 +157,8 @@ const (
DashboardTabYAML DashboardTab = "yaml"
)
// ApplicationDefinitionDashboard describes how this resource appears in the UI.
type ApplicationDefinitionDashboard struct {
// CozystackResourceDefinitionDashboard describes how this resource appears in the UI.
type CozystackResourceDefinitionDashboard struct {
// Human-readable name shown in the UI (e.g., "Bucket")
Singular string `json:"singular"`
// Plural human-readable name (e.g., "Buckets")

View file

@ -132,16 +132,6 @@ 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

View file

@ -21,232 +21,12 @@ limitations under the License.
package v1alpha1
import (
"github.com/fluxcd/helm-controller/api/v2"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
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 *ApplicationDefinition) DeepCopyInto(out *ApplicationDefinition) {
*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 ApplicationDefinition.
func (in *ApplicationDefinition) DeepCopy() *ApplicationDefinition {
if in == nil {
return nil
}
out := new(ApplicationDefinition)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ApplicationDefinition) 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 *ApplicationDefinitionApplication) DeepCopyInto(out *ApplicationDefinitionApplication) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinitionApplication.
func (in *ApplicationDefinitionApplication) DeepCopy() *ApplicationDefinitionApplication {
if in == nil {
return nil
}
out := new(ApplicationDefinitionApplication)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ApplicationDefinitionDashboard) DeepCopyInto(out *ApplicationDefinitionDashboard) {
*out = *in
if in.Tags != nil {
in, out := &in.Tags, &out.Tags
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Tabs != nil {
in, out := &in.Tabs, &out.Tabs
*out = make([]DashboardTab, len(*in))
copy(*out, *in)
}
if in.KeysOrder != nil {
in, out := &in.KeysOrder, &out.KeysOrder
*out = make([][]string, len(*in))
for i := range *in {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = make([]string, len(*in))
copy(*out, *in)
}
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinitionDashboard.
func (in *ApplicationDefinitionDashboard) DeepCopy() *ApplicationDefinitionDashboard {
if in == nil {
return nil
}
out := new(ApplicationDefinitionDashboard)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ApplicationDefinitionList) DeepCopyInto(out *ApplicationDefinitionList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]ApplicationDefinition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinitionList.
func (in *ApplicationDefinitionList) DeepCopy() *ApplicationDefinitionList {
if in == nil {
return nil
}
out := new(ApplicationDefinitionList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ApplicationDefinitionList) 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 *ApplicationDefinitionRelease) DeepCopyInto(out *ApplicationDefinitionRelease) {
*out = *in
if in.ChartRef != nil {
in, out := &in.ChartRef, &out.ChartRef
*out = new(v2.CrossNamespaceSourceReference)
**out = **in
}
if in.Labels != nil {
in, out := &in.Labels, &out.Labels
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinitionRelease.
func (in *ApplicationDefinitionRelease) DeepCopy() *ApplicationDefinitionRelease {
if in == nil {
return nil
}
out := new(ApplicationDefinitionRelease)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ApplicationDefinitionResourceSelector) DeepCopyInto(out *ApplicationDefinitionResourceSelector) {
*out = *in
in.LabelSelector.DeepCopyInto(&out.LabelSelector)
if in.ResourceNames != nil {
in, out := &in.ResourceNames, &out.ResourceNames
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinitionResourceSelector.
func (in *ApplicationDefinitionResourceSelector) DeepCopy() *ApplicationDefinitionResourceSelector {
if in == nil {
return nil
}
out := new(ApplicationDefinitionResourceSelector)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ApplicationDefinitionResources) DeepCopyInto(out *ApplicationDefinitionResources) {
*out = *in
if in.Exclude != nil {
in, out := &in.Exclude, &out.Exclude
*out = make([]*ApplicationDefinitionResourceSelector, len(*in))
for i := range *in {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(ApplicationDefinitionResourceSelector)
(*in).DeepCopyInto(*out)
}
}
}
if in.Include != nil {
in, out := &in.Include, &out.Include
*out = make([]*ApplicationDefinitionResourceSelector, len(*in))
for i := range *in {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(ApplicationDefinitionResourceSelector)
(*in).DeepCopyInto(*out)
}
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinitionResources.
func (in *ApplicationDefinitionResources) DeepCopy() *ApplicationDefinitionResources {
if in == nil {
return nil
}
out := new(ApplicationDefinitionResources)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ApplicationDefinitionSpec) DeepCopyInto(out *ApplicationDefinitionSpec) {
*out = *in
out.Application = in.Application
in.Release.DeepCopyInto(&out.Release)
in.Secrets.DeepCopyInto(&out.Secrets)
in.Services.DeepCopyInto(&out.Services)
in.Ingresses.DeepCopyInto(&out.Ingresses)
if in.Dashboard != nil {
in, out := &in.Dashboard, &out.Dashboard
*out = new(ApplicationDefinitionDashboard)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinitionSpec.
func (in *ApplicationDefinitionSpec) DeepCopy() *ApplicationDefinitionSpec {
if in == nil {
return nil
}
out := new(ApplicationDefinitionSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Component) DeepCopyInto(out *Component) {
*out = *in
@ -297,6 +77,237 @@ func (in *ComponentInstall) DeepCopy() *ComponentInstall {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CozystackResourceDefinition) DeepCopyInto(out *CozystackResourceDefinition) {
*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 CozystackResourceDefinition.
func (in *CozystackResourceDefinition) DeepCopy() *CozystackResourceDefinition {
if in == nil {
return nil
}
out := new(CozystackResourceDefinition)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CozystackResourceDefinition) 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 *CozystackResourceDefinitionApplication) DeepCopyInto(out *CozystackResourceDefinitionApplication) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionApplication.
func (in *CozystackResourceDefinitionApplication) DeepCopy() *CozystackResourceDefinitionApplication {
if in == nil {
return nil
}
out := new(CozystackResourceDefinitionApplication)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CozystackResourceDefinitionChart) DeepCopyInto(out *CozystackResourceDefinitionChart) {
*out = *in
out.SourceRef = in.SourceRef
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionChart.
func (in *CozystackResourceDefinitionChart) DeepCopy() *CozystackResourceDefinitionChart {
if in == nil {
return nil
}
out := new(CozystackResourceDefinitionChart)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CozystackResourceDefinitionDashboard) DeepCopyInto(out *CozystackResourceDefinitionDashboard) {
*out = *in
if in.Tags != nil {
in, out := &in.Tags, &out.Tags
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Tabs != nil {
in, out := &in.Tabs, &out.Tabs
*out = make([]DashboardTab, len(*in))
copy(*out, *in)
}
if in.KeysOrder != nil {
in, out := &in.KeysOrder, &out.KeysOrder
*out = make([][]string, len(*in))
for i := range *in {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = make([]string, len(*in))
copy(*out, *in)
}
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionDashboard.
func (in *CozystackResourceDefinitionDashboard) DeepCopy() *CozystackResourceDefinitionDashboard {
if in == nil {
return nil
}
out := new(CozystackResourceDefinitionDashboard)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CozystackResourceDefinitionList) DeepCopyInto(out *CozystackResourceDefinitionList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]CozystackResourceDefinition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionList.
func (in *CozystackResourceDefinitionList) DeepCopy() *CozystackResourceDefinitionList {
if in == nil {
return nil
}
out := new(CozystackResourceDefinitionList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CozystackResourceDefinitionList) 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 *CozystackResourceDefinitionRelease) DeepCopyInto(out *CozystackResourceDefinitionRelease) {
*out = *in
out.Chart = in.Chart
if in.Labels != nil {
in, out := &in.Labels, &out.Labels
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionRelease.
func (in *CozystackResourceDefinitionRelease) DeepCopy() *CozystackResourceDefinitionRelease {
if in == nil {
return nil
}
out := new(CozystackResourceDefinitionRelease)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CozystackResourceDefinitionResourceSelector) DeepCopyInto(out *CozystackResourceDefinitionResourceSelector) {
*out = *in
in.LabelSelector.DeepCopyInto(&out.LabelSelector)
if in.ResourceNames != nil {
in, out := &in.ResourceNames, &out.ResourceNames
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionResourceSelector.
func (in *CozystackResourceDefinitionResourceSelector) DeepCopy() *CozystackResourceDefinitionResourceSelector {
if in == nil {
return nil
}
out := new(CozystackResourceDefinitionResourceSelector)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CozystackResourceDefinitionResources) DeepCopyInto(out *CozystackResourceDefinitionResources) {
*out = *in
if in.Exclude != nil {
in, out := &in.Exclude, &out.Exclude
*out = make([]*CozystackResourceDefinitionResourceSelector, len(*in))
for i := range *in {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(CozystackResourceDefinitionResourceSelector)
(*in).DeepCopyInto(*out)
}
}
}
if in.Include != nil {
in, out := &in.Include, &out.Include
*out = make([]*CozystackResourceDefinitionResourceSelector, len(*in))
for i := range *in {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(CozystackResourceDefinitionResourceSelector)
(*in).DeepCopyInto(*out)
}
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionResources.
func (in *CozystackResourceDefinitionResources) DeepCopy() *CozystackResourceDefinitionResources {
if in == nil {
return nil
}
out := new(CozystackResourceDefinitionResources)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CozystackResourceDefinitionSpec) DeepCopyInto(out *CozystackResourceDefinitionSpec) {
*out = *in
out.Application = in.Application
in.Release.DeepCopyInto(&out.Release)
in.Secrets.DeepCopyInto(&out.Secrets)
in.Services.DeepCopyInto(&out.Services)
in.Ingresses.DeepCopyInto(&out.Ingresses)
if in.Dashboard != nil {
in, out := &in.Dashboard, &out.Dashboard
*out = new(CozystackResourceDefinitionDashboard)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionSpec.
func (in *CozystackResourceDefinitionSpec) DeepCopy() *CozystackResourceDefinitionSpec {
if in == nil {
return nil
}
out := new(CozystackResourceDefinitionSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DependencyStatus) DeepCopyInto(out *DependencyStatus) {
*out = *in
@ -611,6 +622,21 @@ func (in Selector) DeepCopy() Selector {
return *out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SourceRef) DeepCopyInto(out *SourceRef) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceRef.
func (in *SourceRef) DeepCopy() *SourceRef {
if in == nil {
return nil
}
out := new(SourceRef)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Variant) DeepCopyInto(out *Variant) {
*out = *in

View file

@ -29,8 +29,6 @@ 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/client"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"sigs.k8s.io/controller-runtime/pkg/metrics/filters"
@ -132,11 +130,6 @@ func main() {
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "core.backups.cozystack.io",
Cache: cache.Options{
ByObject: map[client.Object]cache.ByObject{
&backupsv1alpha1.BackupClass{}: {},
},
},
// 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

View file

@ -29,18 +29,14 @@ 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/client"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"sigs.k8s.io/controller-runtime/pkg/metrics/filters"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
"sigs.k8s.io/controller-runtime/pkg/webhook"
strategyv1alpha1 "github.com/cozystack/cozystack/api/backups/strategy/v1alpha1"
backupsv1alpha1 "github.com/cozystack/cozystack/api/backups/v1alpha1"
"github.com/cozystack/cozystack/internal/backupcontroller"
velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
// +kubebuilder:scaffold:imports
)
@ -53,8 +49,6 @@ func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
utilruntime.Must(backupsv1alpha1.AddToScheme(scheme))
utilruntime.Must(strategyv1alpha1.AddToScheme(scheme))
utilruntime.Must(velerov1.AddToScheme(scheme))
// +kubebuilder:scaffold:scheme
}
@ -136,11 +130,6 @@ func main() {
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "strategy.backups.cozystack.io",
Cache: cache.Options{
ByObject: map[client.Object]cache.ByObject{
&backupsv1alpha1.BackupClass{}: {},
},
},
// 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
@ -159,29 +148,10 @@ func main() {
}
if err = (&backupcontroller.BackupJobReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Recorder: mgr.GetEventRecorderFor("backup-controller"),
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "BackupJob")
os.Exit(1)
}
if err = (&backupcontroller.RestoreJobReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Recorder: mgr.GetEventRecorderFor("restore-controller"),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "RestoreJob")
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")
setupLog.Error(err, "unable to create controller", "controller", "Job")
os.Exit(1)
}

View file

@ -23,9 +23,6 @@ import (
"github.com/spf13/cobra"
)
// Version is set at build time via -ldflags.
var Version = "dev"
// rootCmd represents the base command when called without any subcommands.
var rootCmd = &cobra.Command{
Use: "cozypkg",
@ -47,6 +44,6 @@ func Execute() error {
}
func init() {
rootCmd.Version = Version
// Commands are registered in their respective init() functions
}

View file

@ -0,0 +1,29 @@
package main
import (
"flag"
"log"
"net/http"
"path/filepath"
)
func main() {
addr := flag.String("address", ":8123", "Address to listen on")
dir := flag.String("dir", "/cozystack/assets", "Directory to serve files from")
flag.Parse()
absDir, err := filepath.Abs(*dir)
if err != nil {
log.Fatalf("Error getting absolute path for %s: %v", *dir, err)
}
fs := http.FileServer(http.Dir(absDir))
http.Handle("/", fs)
log.Printf("Server starting on %s, serving directory %s", *addr, absDir)
err = http.ListenAndServe(*addr, nil)
if err != nil {
log.Fatalf("Server failed to start: %v", err)
}
}

View file

@ -42,7 +42,6 @@ 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
)
@ -57,7 +56,6 @@ 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
}
@ -70,6 +68,8 @@ func main() {
var disableTelemetry bool
var telemetryEndpoint string
var telemetryInterval string
var cozystackVersion string
var reconcileDeployment bool
var tlsOpts []func(*tls.Config)
flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
"Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
@ -87,6 +87,10 @@ func main() {
"Endpoint for sending telemetry data")
flag.StringVar(&telemetryInterval, "telemetry-interval", "15m",
"Interval between telemetry data collection (e.g. 15m, 1h)")
flag.StringVar(&cozystackVersion, "cozystack-version", "unknown",
"Version of Cozystack")
flag.BoolVar(&reconcileDeployment, "reconcile-deployment", false,
"If set, the Cozystack API server is assumed to run as a Deployment, else as a DaemonSet.")
opts := zap.Options{
Development: false,
}
@ -102,9 +106,10 @@ func main() {
// Configure telemetry
telemetryConfig := telemetry.Config{
Disabled: disableTelemetry,
Endpoint: telemetryEndpoint,
Interval: interval,
Disabled: disableTelemetry,
Endpoint: telemetryEndpoint,
Interval: interval,
CozystackVersion: cozystackVersion,
}
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
@ -195,19 +200,24 @@ func main() {
os.Exit(1)
}
if err = (&controller.ApplicationDefinitionReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
cozyAPIKind := "DaemonSet"
if reconcileDeployment {
cozyAPIKind = "Deployment"
}
if err = (&controller.CozystackResourceDefinitionReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
CozystackAPIKind: cozyAPIKind,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "ApplicationDefinitionReconciler")
setupLog.Error(err, "unable to create controller", "controller", "CozystackResourceDefinitionReconciler")
os.Exit(1)
}
if err = (&controller.ApplicationDefinitionHelmReconciler{
if err = (&controller.CozystackResourceDefinitionHelmReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "ApplicationDefinitionHelmReconciler")
setupLog.Error(err, "unable to create controller", "controller", "CozystackResourceDefinitionHelmReconciler")
os.Exit(1)
}

View file

@ -32,16 +32,12 @@ import (
helmv2 "github.com/fluxcd/helm-controller/api/v2"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
sourcewatcherv1beta1 "github.com/fluxcd/source-watcher/api/v2/v1beta1"
corev1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
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/client"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log"
@ -49,11 +45,8 @@ import (
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"github.com/cozystack/cozystack/internal/cozyvaluesreplicator"
"github.com/cozystack/cozystack/internal/crdinstall"
"github.com/cozystack/cozystack/internal/fluxinstall"
"github.com/cozystack/cozystack/internal/operator"
"github.com/cozystack/cozystack/internal/telemetry"
// +kubebuilder:scaffold:imports
)
@ -78,14 +71,8 @@ func main() {
var probeAddr string
var secureMetrics bool
var enableHTTP2 bool
var installCRDs bool
var installFlux bool
var disableTelemetry bool
var telemetryEndpoint string
var telemetryInterval string
var cozyValuesSecretName string
var cozyValuesSecretNamespace string
var cozyValuesNamespaceSelector string
var cozystackVersion string
var platformSourceURL string
var platformSourceName string
var platformSourceRef string
@ -99,20 +86,12 @@ func main() {
"If set the metrics endpoint is served securely")
flag.BoolVar(&enableHTTP2, "enable-http2", false,
"If set, HTTP/2 will be enabled for the metrics and webhook servers")
flag.BoolVar(&installCRDs, "install-crds", false, "Install Cozystack CRDs before starting reconcile loop")
flag.BoolVar(&installFlux, "install-flux", false, "Install Flux components before starting reconcile loop")
flag.BoolVar(&disableTelemetry, "disable-telemetry", false,
"Disable telemetry collection")
flag.StringVar(&telemetryEndpoint, "telemetry-endpoint", "https://telemetry.cozystack.io",
"Endpoint for sending telemetry data")
flag.StringVar(&telemetryInterval, "telemetry-interval", "15m",
"Interval between telemetry data collection (e.g. 15m, 1h)")
flag.StringVar(&cozystackVersion, "cozystack-version", "unknown",
"Version of Cozystack")
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-platform", "Name for the generated platform source resource and PackageSource")
flag.StringVar(&platformSourceName, "platform-source-name", "cozystack-packages", "Name for the generated platform source resource (default: cozystack-packages)")
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.")
flag.StringVar(&cozyValuesNamespaceSelector, "cozy-values-namespace-selector", "cozystack.io/system=true", "The label selector for namespaces where the cluster-wide configuration values must be replicated.")
opts := zap.Options{
Development: true,
@ -131,28 +110,10 @@ func main() {
os.Exit(1)
}
targetNSSelector, err := labels.Parse(cozyValuesNamespaceSelector)
if err != nil {
setupLog.Error(err, "could not parse namespace label selector")
os.Exit(1)
}
// Initialize the controller manager
// Start the controller manager
setupLog.Info("Starting controller manager")
mgr, err := ctrl.NewManager(config, ctrl.Options{
Scheme: scheme,
Cache: cache.Options{
ByObject: map[client.Object]cache.ByObject{
// Cache only Secrets named <secretName> (in any namespace)
&corev1.Secret{}: {
Field: fields.OneTermEqualSelector("metadata.name", cozyValuesSecretName),
},
// Cache only Namespaces that match a label selector
&corev1.Namespace{}: {
Label: targetNSSelector,
},
},
},
Metrics: metricsserver.Options{
BindAddress: metricsAddr,
SecureServing: secureMetrics,
@ -179,26 +140,10 @@ func main() {
os.Exit(1)
}
// Set up signal handler early so install phases respect SIGTERM
mgrCtx := ctrl.SetupSignalHandler()
// Install Cozystack CRDs before starting reconcile loop
if installCRDs {
setupLog.Info("Installing Cozystack CRDs before starting reconcile loop")
installCtx, installCancel := context.WithTimeout(mgrCtx, 2*time.Minute)
defer installCancel()
if err := crdinstall.Install(installCtx, directClient, crdinstall.WriteEmbeddedManifests); err != nil {
setupLog.Error(err, "failed to install CRDs")
os.Exit(1)
}
setupLog.Info("CRD installation completed successfully")
}
// Install Flux before starting reconcile loop
if installFlux {
setupLog.Info("Installing Flux components before starting reconcile loop")
installCtx, installCancel := context.WithTimeout(mgrCtx, 5*time.Minute)
installCtx, installCancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer installCancel()
// Use direct client for pre-start operations (cache is not ready yet)
@ -212,7 +157,7 @@ func main() {
// Generate and install platform source resource if specified
if platformSourceURL != "" {
setupLog.Info("Generating platform source resource", "url", platformSourceURL, "name", platformSourceName, "ref", platformSourceRef)
installCtx, installCancel := context.WithTimeout(mgrCtx, 2*time.Minute)
installCtx, installCancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer installCancel()
// Use direct client for pre-start operations (cache is not ready yet)
@ -224,29 +169,6 @@ 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(),
@ -265,18 +187,6 @@ func main() {
os.Exit(1)
}
// Setup CozyValuesReplicator reconciler
if err := (&cozyvaluesreplicator.SecretReplicatorReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
SourceNamespace: cozyValuesSecretNamespace,
SecretName: cozyValuesSecretName,
TargetNamespaceSelector: targetNSSelector,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "CozyValuesReplicator")
os.Exit(1)
}
// +kubebuilder:scaffold:builder
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
@ -288,35 +198,8 @@ func main() {
os.Exit(1)
}
// Parse telemetry interval
interval, err := time.ParseDuration(telemetryInterval)
if err != nil {
setupLog.Error(err, "invalid telemetry interval")
os.Exit(1)
}
// Configure telemetry
telemetryConfig := telemetry.Config{
Disabled: disableTelemetry,
Endpoint: telemetryEndpoint,
Interval: interval,
}
// Initialize telemetry collector
// Use APIReader (non-cached) because the manager's cache is filtered
// and doesn't include resources needed for telemetry (e.g., kube-system namespace, nodes, etc.)
collector, err := telemetry.NewOperatorCollector(mgr.GetAPIReader(), &telemetryConfig, config)
if err != nil {
setupLog.V(1).Info("unable to create telemetry collector, telemetry will be disabled", "error", err)
}
if collector != nil {
if err := mgr.Add(collector); err != nil {
setupLog.V(1).Info("unable to set up telemetry collector, continuing without telemetry", "error", err)
}
}
setupLog.Info("Starting controller manager")
mgrCtx := ctrl.SetupSignalHandler()
if err := mgr.Start(mgrCtx); err != nil {
setupLog.Error(err, "problem running manager")
os.Exit(1)
@ -575,79 +458,3 @@ 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
}

View file

@ -1,574 +0,0 @@
/*
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")
}
}

View file

@ -1,151 +0,0 @@
/*
Copyright 2025.
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 (
"crypto/tls"
"flag"
"os"
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that exec-entrypoint and run can make use of them.
_ "k8s.io/client-go/plugin/pkg/client/auth"
helmv2 "github.com/fluxcd/helm-controller/api/v2"
"k8s.io/apimachinery/pkg/runtime"
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/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
"github.com/cozystack/cozystack/internal/controller/fluxplunger"
// +kubebuilder:scaffold:imports
)
var (
scheme = runtime.NewScheme()
setupLog = ctrl.Log.WithName("setup")
)
func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
utilruntime.Must(helmv2.AddToScheme(scheme))
// +kubebuilder:scaffold:scheme
}
func main() {
var metricsAddr string
var enableLeaderElection bool
var probeAddr string
var secureMetrics bool
var enableHTTP2 bool
var tlsOpts []func(*tls.Config)
flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
"Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
"Enable leader election for controller manager. "+
"Enabling this will ensure there is only one active controller manager.")
flag.BoolVar(&secureMetrics, "metrics-secure", true,
"If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.")
flag.BoolVar(&enableHTTP2, "enable-http2", false,
"If set, HTTP/2 will be enabled for the metrics server")
opts := zap.Options{
Development: false,
}
opts.BindFlags(flag.CommandLine)
flag.Parse()
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
// if the enable-http2 flag is false (the default), http/2 should be disabled
// due to its vulnerabilities. More specifically, disabling http/2 will
// prevent from being vulnerable to the HTTP/2 Stream Cancellation and
// Rapid Reset CVEs. For more information see:
// - https://github.com/advisories/GHSA-qppj-fm5r-hxr3
// - https://github.com/advisories/GHSA-4374-p667-p6c8
disableHTTP2 := func(c *tls.Config) {
setupLog.Info("disabling http/2")
c.NextProtos = []string{"http/1.1"}
}
if !enableHTTP2 {
tlsOpts = append(tlsOpts, disableHTTP2)
}
// Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server.
// More info:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/metrics/server
// - https://book.kubebuilder.io/reference/metrics.html
metricsServerOptions := metricsserver.Options{
BindAddress: metricsAddr,
SecureServing: secureMetrics,
TLSOpts: tlsOpts,
}
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
Metrics: metricsServerOptions,
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "flux-plunger.cozystack.io",
// 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
// speeds up voluntary leader transitions as the new leader don't have to wait
// LeaseDuration time first.
//
// In the default scaffold provided, the program ends immediately after
// the manager stops, so would be fine to enable this option. However,
// if you are doing or is intended to do any operation such as perform cleanups
// after the manager stops then its usage might be unsafe.
// LeaderElectionReleaseOnCancel: true,
})
if err != nil {
setupLog.Error(err, "unable to create manager")
os.Exit(1)
}
if err = (&fluxplunger.FluxPlunger{
Client: mgr.GetClient(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "FluxPlunger")
os.Exit(1)
}
// +kubebuilder:scaffold:builder
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up health check")
os.Exit(1)
}
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up ready check")
os.Exit(1)
}
setupLog.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
setupLog.Error(err, "problem running manager")
os.Exit(1)
}
}

View file

@ -29,7 +29,6 @@ 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"
@ -132,11 +131,6 @@ 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

View file

@ -1,819 +0,0 @@
{
"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": {}
}

File diff suppressed because it is too large Load diff

View file

@ -1,957 +0,0 @@
{
"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": {}
}

View file

@ -1,637 +0,0 @@
{
"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": {}
}

File diff suppressed because it is too large Load diff

View file

@ -1,602 +0,0 @@
{
"__inputs": [
{
"name": "DS_PROMETHEUS",
"label": "Prometheus",
"description": "",
"type": "datasource",
"pluginId": "prometheus",
"pluginName": "Prometheus"
}
],
"__elements": {},
"__requires": [
{
"type": "panel",
"id": "bargauge",
"name": "Bar gauge",
"version": ""
},
{
"type": "grafana",
"id": "grafana",
"name": "Grafana",
"version": "9.4.7"
},
{
"type": "datasource",
"id": "prometheus",
"name": "Prometheus",
"version": "1.0.0"
},
{
"type": "panel",
"id": "timeseries",
"name": "Time series",
"version": ""
}
],
"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": "",
"editable": true,
"fiscalYearStartMonth": 0,
"gnetId": 16612,
"graphTooltip": 0,
"id": null,
"links": [
{
"asDropdown": true,
"icon": "external link",
"includeVars": true,
"keepTime": true,
"tags": [
"cilium-overview"
],
"targetBlank": false,
"title": "Cilium Overviews",
"tooltip": "",
"type": "dashboards",
"url": ""
},
{
"asDropdown": true,
"icon": "external link",
"includeVars": false,
"keepTime": true,
"tags": [
"hubble"
],
"targetBlank": false,
"title": "Hubble",
"tooltip": "",
"type": "dashboards",
"url": ""
}
],
"liveNow": false,
"panels": [
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 0
},
"id": 2,
"panels": [],
"title": "DNS",
"type": "row"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "normal"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"min": 0,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "reqps"
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 12,
"x": 0,
"y": 1
},
"id": 37,
"options": {
"legend": {
"calcs": [
"mean",
"lastNotNull"
],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "sum(rate(hubble_dns_queries_total{cluster=~\"$cluster\", source_namespace=~\"$source_namespace\", destination_namespace=~\"$destination_namespace\"}[$__rate_interval])) by (source) > 0",
"legendFormat": "{{source}}",
"range": true,
"refId": "A"
}
],
"title": "DNS queries",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"min": 0,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "reqps"
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 12,
"x": 12,
"y": 1
},
"id": 41,
"options": {
"displayMode": "gradient",
"minVizHeight": 10,
"minVizWidth": 0,
"orientation": "horizontal",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"showUnfilled": true
},
"pluginVersion": "9.4.7",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "topk(10, sum(rate(hubble_dns_queries_total{cluster=~\"$cluster\", source_namespace=~\"$source_namespace\", destination_namespace=~\"$destination_namespace\"}[$__rate_interval])*60) by (query))",
"legendFormat": "{{query}}",
"range": true,
"refId": "A"
}
],
"title": "Top 10 DNS queries",
"type": "bargauge"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "normal"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"min": 0,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "reqps"
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 12,
"x": 0,
"y": 10
},
"id": 39,
"options": {
"legend": {
"calcs": [
"mean",
"lastNotNull"
],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "round(sum(rate(hubble_dns_queries_total{cluster=~\"$cluster\", source_namespace=~\"$source_namespace\", destination_namespace=~\"$destination_namespace\"}[$__rate_interval])) by (source) - sum(label_replace(sum(rate(hubble_dns_responses_total{cluster=~\"$cluster\", source_namespace=~\"$destination_namespace\", destination_namespace=~\"$source_namespace\"}[$__rate_interval])) by (destination), \"source\", \"$1\", \"destination\", \"(.*)\")) without (destination), 0.001) > 0",
"legendFormat": "{{source}}",
"range": true,
"refId": "A"
}
],
"title": "Missing DNS responses",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "normal"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"min": 0,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "reqps"
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 12,
"x": 12,
"y": 10
},
"id": 43,
"options": {
"legend": {
"calcs": [
"mean",
"lastNotNull"
],
"displayMode": "table",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "sum(rate(hubble_dns_responses_total{cluster=~\"$cluster\", source_namespace=~\"$destination_namespace\", destination_namespace=~\"$source_namespace\", rcode!=\"No Error\"}[$__rate_interval])) by (destination, rcode) > 0",
"legendFormat": "{{destination}}: {{rcode}}",
"range": true,
"refId": "A"
}
],
"title": "DNS errors",
"type": "timeseries"
}
],
"refresh": "",
"revision": 1,
"schemaVersion": 38,
"style": "dark",
"tags": [
"kubecon-demo"
],
"templating": {
"list": [
{
"current": {
"selected": false,
"text": "default",
"value": "default"
},
"hide": 0,
"includeAll": false,
"label": "Data Source",
"multi": false,
"name": "DS_PROMETHEUS",
"options": [],
"query": "prometheus",
"queryValue": "",
"refresh": 1,
"regex": "(?!grafanacloud-usage|grafanacloud-ml-metrics).+",
"skipUrlSync": false,
"type": "datasource"
},
{
"current": {},
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"definition": "label_values(cilium_version, cluster)",
"hide": 0,
"includeAll": true,
"multi": true,
"name": "cluster",
"options": [],
"query": {
"query": "label_values(cilium_version, cluster)",
"refId": "StandardVariableQuery"
},
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 0,
"type": "query"
},
{
"allValue": ".*",
"current": {},
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"definition": "label_values(source_namespace)",
"hide": 0,
"includeAll": true,
"label": "Source Namespace",
"multi": true,
"name": "source_namespace",
"options": [],
"query": {
"query": "label_values(source_namespace)",
"refId": "StandardVariableQuery"
},
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 0,
"type": "query"
},
{
"allValue": ".*",
"current": {},
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"definition": "label_values(destination_namespace)",
"hide": 0,
"includeAll": true,
"label": "Destination Namespace",
"multi": true,
"name": "destination_namespace",
"options": [],
"query": {
"query": "label_values(destination_namespace)",
"refId": "StandardVariableQuery"
},
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 0,
"type": "query"
}
]
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "",
"title": "Hubble / DNS Overview (Namespace)",
"uid": "_f0DUpY4k",
"version": 26,
"weekStart": ""
}

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more