Compare commits

..

9 commits

Author SHA1 Message Date
Aleksei Sviridkin
10b9cdd481
fix(security): drop USER 65532 from cozystack-api and lineage-controller-webhook
The previous commit (6498d37b) added `USER 65532:65532` to seven
first-party scratch images. Two of them — cozystack-api and
lineage-controller-webhook — mount their TLS serving certs as a
secret with `defaultMode: 0400` (root-only). With USER 65532, the
process can no longer read `tls.crt` / `tls.key` and the webhook
pod fails to start, manifesting downstream as
`failed calling webhook "lineage.cozystack.io": dial tcp <ServiceIP>:443: connect: operation not permitted`
during install of the tenant-root HelmRelease in e2e.

Reverts the USER directive on those two images. Properly switching them
to non-root requires either flipping the secret mount to
`defaultMode: 0440` plus `securityContext.fsGroup: 65532` on the
pod, or relaxing to 0444 — both choices touch the runtime chart and
are out of scope for a Go-deps security PR. Tracked as a follow-up.

The remaining five hardened images (backup-controller,
backupstrategy-controller, token-proxy, flux-plunger, kubeovn-plunger)
do not mount root-only secrets and continue to run as 65532.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-04-26 04:41:27 +03:00
Aleksei Sviridkin
6498d37be7
fix(security): run first-party scratch images as non-root UID 65532
CodeRabbit (PR review on #2494) flagged that the first-party scratch
images leave the container process running as root because no USER
directive is set. Adds the standard distroless nonroot UID/GID
65532:65532 just before ENTRYPOINT in seven first-party Dockerfiles.

The chosen UID matches gcr.io/distroless/static:nonroot and the
existing convention in the kubevirt-csi-driver and kamaji images.
Statically-linked Go binaries copied with default permissions remain
world-readable, so they execute fine under the non-root identity
without an extra chown step in the builder stage.

Touched:
- packages/system/backup-controller/images/backup-controller/Dockerfile
- packages/system/backupstrategy-controller/images/backupstrategy-controller/Dockerfile
- packages/system/cozystack-api/images/cozystack-api/Dockerfile
- packages/system/dashboard/images/token-proxy/Dockerfile
- packages/system/flux-plunger/images/flux-plunger/Dockerfile
- packages/system/kubeovn-plunger/images/kubeovn-plunger/Dockerfile
- packages/system/lineage-controller-webhook/images/lineage-controller-webhook/Dockerfile

Vendored Dockerfiles (kubevirt-cloud-provider, redis-operator,
keycloak-operator, linstor-csi, multus-cni, objectstorage,
s3manager, kamaji, cluster-autoscaler) are intentionally left
untouched — their runtime user choice is upstream's call.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-04-26 03:45:14 +03:00
Aleksei Sviridkin
44e5ae508b
fix(security): align OpenTelemetry exporter modules with otel core
CodeRabbit (PR review on #2494) flagged that go.opentelemetry.io/otel
core / sdk / metric / trace are at v1.43.0 in the root module while
the otlptrace and otlptracegrpc exporters are still pinned at v1.34.0.
The exporter line declares a hard dependency on a matching SDK minor,
so mixing v1.34.x exporters with v1.43.x core would break version
resolution if the exporter code path were exercised.

Bumps to latest stable across the otel surface:
- go.opentelemetry.io/otel/exporters/otlp/otlptrace 1.34.0 -> 1.43.0
- go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
  1.34.0 -> 1.43.0
- go.opentelemetry.io/proto/otlp 1.5.0 -> 1.10.0
- go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc
  0.61.0 -> 0.68.0
- go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp
  0.61.0 -> 0.68.0

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-04-26 03:44:07 +03:00
Aleksei Sviridkin
3fea7cd487
fix(security): bump first-party Dockerfile builders to golang:1.26
The root go.mod now declares go 1.26.2 (raised in the first commit
of this PR). Every Dockerfile that copies the root go.mod and builds
a cozystack-owned binary has to use a matching base image, otherwise
the in-container go toolchain has to download go 1.26 on the fly
(via GOTOOLCHAIN=auto) at every build, which slows builds and breaks
in network-restricted CI sandboxes.

Bumps:
- packages/core/installer/images/cozystack-operator
- packages/system/backup-controller/images/backup-controller
- packages/system/backupstrategy-controller/images/backupstrategy-controller
- packages/system/cozystack-api/images/cozystack-api
- packages/system/cozystack-controller/images/cozystack-controller
- packages/system/flux-plunger/images/flux-plunger
- packages/system/kubeovn-plunger/images/kubeovn-plunger
- packages/system/lineage-controller-webhook/images/lineage-controller-webhook

All eight images go from golang:1.25-alpine to golang:1.26-alpine.
Also normalizes the cozystack-operator Dockerfile FROM stage keyword
from lowercase 'as' to uppercase 'AS' while touching it.

Vendored Dockerfiles (kubevirt-cloud-provider, redis-operator,
keycloak-operator, linstor-csi, multus-cni, objectstorage,
s3manager, kamaji) are intentionally untouched: they pull upstream
sources at build time and pin their go base image to whatever the
upstream commit was tested against.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-04-26 01:53:19 +03:00
Aleksei Sviridkin
9e0e093c30
fix(security): bump token-proxy deps to latest
The token-proxy is a first-party Go binary (not vendored) used by
the cozystack dashboard for JWT-validated reverse-proxy access to
tenant-scoped API endpoints.

Bumps:
- github.com/lestrrat-go/jwx/v3 3.0.13 -> 3.1.0
- github.com/lestrrat-go/httprc/v3 3.0.2 -> 3.0.5
- golang.org/x/crypto 0.46.0 -> 0.50.0
- golang.org/x/sys 0.39.0 -> 0.43.0
- go directive 1.24.0 -> 1.26.2
- builder image golang:1.24-alpine -> golang:1.26-alpine

The jwx 3.0 -> 3.1 line restructured an internal package
(internal/jwxio); only library internals are affected and main.go
needs no source changes. Also normalizes the Dockerfile FROM stage
keyword to uppercase AS while touching it.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-04-26 01:50:23 +03:00
Aleksei Sviridkin
dd486fa95f
fix(security): bump api/apps/v1alpha1 deps to latest
This module exposes the apps/v1alpha1 Go types to external consumers
and was missed in the initial sweep. Bumps it in line with the rest
of the security PR.

Bumps:
- k8s.io/apimachinery 0.35.2 -> 0.36.0
- golang.org/x/net 0.47.0 -> 0.53.0
- golang.org/x/text 0.31.0 -> 0.36.0
- go directive 1.25.0 -> 1.26.2

All bumps are transitive (the module itself only declares
k8s.io/apimachinery directly). No source changes required.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-04-26 01:50:14 +03:00
Aleksei Sviridkin
235d194e4a
fix(security): bump kubeovn-webhook to latest deps
Bumps all CVE-flagged dependencies in kubeovn-webhook to their latest
stable versions.

CVE-flagged bumps:
- golang.org/x/net -> 0.53.0 (GHSA-vvpx-j8f3-3w6h high,
  GHSA-4374-p667-p6c8 high, GHSA-2wrh-6pvc-2jm9 medium,
  GHSA-qppj-fm5r-hxr3 medium, GHSA-4v7x-pqxf-cx7m medium,
  GHSA-xrjj-mj9h-534m medium, GHSA-qxp5-gwg8-xv66 medium,
  GHSA-vvgc-356p-c3xw medium)
- golang.org/x/oauth2 -> 0.36.0 (GHSA-6v2p-p543-phr9, high)
- google.golang.org/protobuf -> 1.36.11 (GHSA-8r3f-844c-mc37, medium)

Cascading bumps required to consume the latest x/net and x/oauth2
(which require Go 1.25+):
- go directive 1.23 -> 1.26.2
- builder image golang:1.23 -> golang:1.26

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-04-26 01:46:14 +03:00
Aleksei Sviridkin
0fa1ca5f03
fix(security): bump kubevirt-csi-driver to latest deps
Bumps all CVE-flagged dependencies in the kubevirt-csi-driver wrapper
to their latest stable versions and adapts the local main.go to the
new upstream service.NewKubevirtCSIDriver builder API.

CVE-flagged bumps:
- google.golang.org/grpc 1.65.0 -> 1.80.0 (GHSA-p77j-4mvh-x3m3, critical)
- github.com/opencontainers/runc -> 1.4.2 (GHSA-cgrx-mc8f-2prm,
  GHSA-qw9x-cqr3-wc7r, GHSA-9493-h29p-rfm2 high, GHSA-jfvp-7x6p-h2pv
  medium)
- golang.org/x/oauth2 -> 0.36.0 (GHSA-6v2p-p543-phr9, high)
- golang.org/x/net -> 0.53.0 (GHSA-vvgc-356p-c3xw, GHSA-qxp5-gwg8-xv66
  medium)

Cascading bumps required to consume runc 1.4.2 (which removed
libcontainer/userns) and the latest x/net (which requires Go 1.25+):
- k8s.io/* replace block 0.31.4 -> 0.36.0
- kubevirt.io/csi-driver upstream pin -> 20260424143118-bee6c348c004
- go directive 1.22.0 -> 1.26.2
- builder image golang:1.22.5 -> golang:1.26

The upstream csi-driver split NewKubevirtCSIDriver into a builder
chain (WithIdentityService / WithControllerService / WithNodeService);
main.go is rewritten to call the new entry points and gate them on
the runControllerService and runNodeService flags as before.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-04-26 01:46:14 +03:00
Aleksei Sviridkin
40558f1ac7
fix(security): bump root module Go deps to latest
Bumps all CVE-flagged transitive dependencies in the root go.mod to
their latest stable versions. Also raises the go directive to 1.26.2
to match the toolchain required by upgraded golang.org/x/* and
opentelemetry packages.

CVE-flagged bumps:
- google.golang.org/grpc 1.73.0 -> 1.80.0 (GHSA-p77j-4mvh-x3m3, critical)
- go.opentelemetry.io/otel 1.37.0 -> 1.43.0 (GHSA-mh2q-q3fh-2475 high,
  GHSA-9h8m-3fm2-qjrq high, GHSA-hfvc-g4fc-pqhx high)
- go.opentelemetry.io/otel/sdk 1.37.0 -> 1.43.0 (same advisories)
- golang.org/x/crypto 0.42.0 -> 0.50.0 (GHSA-f6x5-jh6r-wrfv,
  GHSA-j5w8-q4qc-rx2x medium)
- github.com/moby/spdystream 0.5.0 -> 0.5.1 (GHSA-pc3f-x583-g7j2, high)

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-04-26 01:45:51 +03:00
188 changed files with 744 additions and 13624 deletions

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,10 +1,9 @@
<!-- 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
- Scopes for system components: dashboard, platform, cilium, kube-ovn, linstor, fluxcd, cluster-api
- Scopes for managed apps: postgres, mariadb, redis, kafka, clickhouse, virtual-machine, kubernetes
- Scopes for development and maintenance: api, hack, tests, ci, docs, maintenance
- Breaking changes: append `!` after type/scope (`feat(api)!: ...`) or add a `BREAKING CHANGE:` footer
- 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.

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

@ -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

@ -223,7 +223,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
});
@ -411,7 +411,7 @@ jobs:
repo: context.repo.repo,
head: changelogBranch,
base: baseBranch,
title: `docs(release): add changelog for v${version}`,
title: `docs: 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
});
@ -421,7 +421,7 @@ jobs:
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.data.number,
labels: ['kind/documentation', 'automated']
labels: ['documentation', 'automated']
});
console.log(`Created PR #${pr.data.number} for changelog`);

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)

View file

@ -22,7 +22,6 @@ build: build-deps
make -C packages/system/lineage-controller-webhook image
make -C packages/system/cilium image
make -C packages/system/linstor image
make -C packages/system/linstor-gui image
make -C packages/system/kubeovn-webhook image
make -C packages/system/kubeovn-plunger image
make -C packages/system/dashboard image
@ -83,19 +82,11 @@ 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 bats-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.

View file

@ -1,8 +1,8 @@
module github.com/cozystack/cozystack/api/apps/v1alpha1
go 1.25.0
go 1.26.2
require k8s.io/apimachinery v0.35.2
require k8s.io/apimachinery v0.36.0
require (
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
@ -12,13 +12,13 @@ require (
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
golang.org/x/net v0.53.0 // indirect
golang.org/x/text v0.36.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
k8s.io/klog/v2 v2.140.0 // indirect
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // 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
sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect
)

View file

@ -1,6 +1,7 @@
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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/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=
@ -16,8 +17,9 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/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=
@ -28,29 +30,29 @@ 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=
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
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=
k8s.io/apimachinery v0.36.0 h1:jZyPzhd5Z+3h9vJLt0z9XdzW9VzNzWAUw+P1xZ9PXtQ=
k8s.io/apimachinery v0.36.0/go.mod h1:FklypaRJt6n5wUIwWXIP6GJlIpUizTgfo1T/As+Tyxc=
k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc=
k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0=
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a h1:xCeOEAOoGYl2jnJoHkC3hkbPJgdATINPMAxaynU2Ovg=
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a/go.mod h1:uGBT7iTA6c6MvqUvSXIaYZo9ukscABYi2btjhvgKGZ0=
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU=
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
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/structured-merge-diff/v6 v6.3.2 h1:kwVWMx5yS1CrnFWA/2QHyRVJ8jM6dBA80uLmm0wJkk8=
sigs.k8s.io/structured-merge-diff/v6 v6.3.2/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

@ -36,9 +36,6 @@ type ConfigSpec struct {
// 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 {
@ -69,9 +66,6 @@ type Addons struct {
// 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"`
@ -163,21 +157,6 @@ type GatewayAPIAddon struct {
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

View file

@ -49,7 +49,6 @@ func (in *Addons) DeepCopyInto(out *Addons) {
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)
@ -136,7 +135,6 @@ func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) {
}
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.
@ -262,37 +260,6 @@ func (in *GatewayAPIAddon) DeepCopy() *GatewayAPIAddon {
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

View file

@ -92,9 +92,6 @@ type Bootstrap struct {
// 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 {

View file

@ -26,9 +26,6 @@ type ConfigSpec struct {
// 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"`

View file

@ -620,11 +620,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

@ -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

@ -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

@ -43,11 +43,10 @@ git commit --signoff -m "type(scope): brief description"
**Types:** `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`
**Scopes** (examples — not an exhaustive list; pick the most specific scope that describes the change, and introduce a new one if a genuinely new area needs its own):
- System, e.g.: `dashboard`, `platform`, `operator`, `cilium`, `kube-ovn`, `linstor`, `fluxcd`, `cluster-api`
- Apps, e.g.: `postgres`, `mariadb`, `redis`, `kafka`, `clickhouse`, `virtual-machine`, `kubernetes`
- Other, e.g.: `api`, `hack`, `tests`, `ci`, `docs`, `agents`, `maintenance`
**Scopes** (e.g., not exhaustive — use any scope that names the component you are touching):
- System: `dashboard`, `platform`, `cilium`, `kube-ovn`, `linstor`, `fluxcd`, `cluster-api`
- Apps: `postgres`, `mariadb`, `redis`, `kafka`, `clickhouse`, `virtual-machine`, `kubernetes`
- Other: `api`, `hack`, `tests`, `ci`, `docs`, `agents`, `maintenance`
Breaking changes: append `!` after type/scope (`feat(api)!: ...`) or add a `BREAKING CHANGE:` footer.
@ -58,49 +57,6 @@ git commit --signoff -m "fix(postgres): update operator to version 1.2.3"
git commit --signoff -m "docs(contributing): add installation guide"
```
## PR Title Auto-Labeling
`.github/workflows/pr-labeler.yaml` parses the PR title on `opened`, `edited`, `reopened`, and `synchronize` events and applies labels additively (never removes). The title is expected to follow Conventional Commits — same format as commit messages above.
**Type → `kind/*`:**
| type | label |
| --------- | ------------------ |
| feat | kind/feature |
| fix | kind/bug |
| docs | kind/documentation |
| chore | kind/cleanup |
| refactor | kind/cleanup |
| style, perf, test, build, ci, revert | (no kind label) |
**Scope → `area/*`** (full mapping in `.github/workflows/pr-labeler.yaml`):
| scope (examples) | label |
| --- | --- |
| agents, ai | area/ai |
| api, cozystack-api | area/api |
| build | area/build |
| ci | area/ci |
| dashboard | area/dashboard |
| postgres, mariadb, redis, etcd, kafka, clickhouse, postgres-operator, mariadb-operator | area/database |
| extra | area/extra |
| kubernetes | area/kubernetes |
| monitoring, vlogs, vmstack, grafana, workloadmonitor | area/monitoring |
| ingress, gateway, vpn, metallb, cilium, kube-ovn, cozy-proxy, ... | area/networking |
| platform, bundle, flux, fluxcd, cluster-api, talos, installer, cozyctl, cozystack-engine, cozy-lib | area/platform |
| backport, release | area/release |
| seaweedfs, bucket, linstor, velero, harbor, backups | area/storage |
| tests, e2e | area/testing |
| kubevirt, cdi, vmi, vm-import, virtual-machine, hami, gpu-operator | area/virtualization |
**Special handling:**
- `[Backport release-1.x]` prefix is stripped before parsing; `area/release` and `backport` labels are added.
- Composite scope (`feat(platform, system, apps): ...`) — each comma-separated part is mapped independently.
- `!` after type or `BREAKING CHANGE:` footer in the body → `kind/breaking-change`.
- Unmapped scope or non-conventional title → `area/uncategorized` (signals the PR needs manual area selection).
- Bracket-style fallback (`[scope] description`) maps `scope``area/*` but cannot infer `kind/*`.
### AI Agent Attribution
When an AI agent authors or materially assists with a commit, add an `Assisted-By:` trailer naming the model:

View file

@ -83,14 +83,6 @@ packages/<category>/<package-name>/
- Reference PR numbers when available
- Keep commits atomic and focused
### PackageSource CRD upgrade policy
Each component in a `PackageSource` may set `install.upgradeCRDs` to control how CRDs from the chart's `crds/` directory are handled on `HelmRelease` upgrades. Allowed values: `Skip` (default — helm-controller does not touch CRDs on upgrade), `Create` (create new CRDs only), `CreateReplace` (create new and overwrite existing).
Set `upgradeCRDs: CreateReplace` for operators whose upstream regularly adds new CRDs between versions (etcd-operator, cnpg, kubevirt, kamaji). Without it, new CRDs from a chart bump do not land on existing clusters — only fresh installs get them.
Do **not** set `CreateReplace` blindly: it overwrites every CRD in `crds/` and can cause silent data loss if upstream drops a field from a CRD that has live objects. Only enable it for operators whose schema evolution is additive-only. When in doubt, leave it unset and apply new CRDs manually.
### Documentation
Documentation is organized as follows:

View file

@ -1,37 +0,0 @@
<!--
https://github.com/cozystack/cozystack/releases/tag/v1.3.1
-->
# v1.3.1 (2026-04-28)
Patch release covering a TenantNamespace IDOR fix in the API, a destructive `post-upgrade` hook removed from the etcd chart, kamaji controller stability, a `linstor-csi` bump that fixes live migration on Protocol-A/B DRBD resources, the missing `linstor-gui` build wiring, and a velero RBAC fix that unblocked installs on bundles without Velero.
## Security
* **fix(api): prevent IDOR in TenantNamespace Get and Watch handlers**: Two IDOR (Insecure Direct Object Reference) vulnerabilities allowed authenticated users to read TenantNamespace metadata they had no RoleBinding for. The `Get` and `Watch` handlers now go through a new `hasAccessToNamespace()` helper that lists RoleBindings scoped only to the target namespace (orders of magnitude faster than the previous all-cluster scan), returns `NotFound` instead of leaking existence on unauthorized access, and applies the same check on the `Watch` filter path. Includes regression tests for the unauthorized paths. ([**@IvanHunters**](https://github.com/IvanHunters) in #2471, backport #2524)
## Features
* **feat(linstor): bump linstor-csi to v1.10.6 with Protocol-C dual-attach fix**: Live migration of KubeVirt VMs on Protocol-A/B (async) DRBD volumes no longer fails with `Protocol C required`. `linstor-csi` v1.10.6 now installs a `Protocol=C` override on the resource-definition during dual-attach and reverts it on detach, so `replicated-async` StorageClasses and other Protocol-A/B resource groups support live migration without manual `drbdadm adjust` intervention. ([**@kvaps**](https://github.com/kvaps) in #2496, backport #2505)
## Fixes
* **fix(backups): move velero-configmap Role to velero chart**: The `backupstrategy-controller` (a default package) declared a Role/RoleBinding scoped to the `cozy-velero` namespace for managing `ResourceModifier` ConfigMaps. On bundles where Velero was not enabled, that namespace did not exist and the HelmRelease failed with `namespaces "cozy-velero" not found`, blocking installation. The Role/RoleBinding now lives in the velero chart, so it is created only when velero is actually deployed. ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2459, backport #2467)
* **fix(etcd): remove destructive post-upgrade cert-regeneration hook**: The etcd chart ran a `post-upgrade` Helm hook on every upgrade that deleted etcd TLS Secrets (`etcd-ca-tls`, `etcd-peer-ca-tls`, `etcd-client-tls`, `etcd-peer-tls`, `etcd-server-tls`) and then deleted etcd pods, forcing cert-manager to re-issue the entire etcd CA chain. On clusters with Kamaji-managed tenant control planes this put every tenant `kube-apiserver` into CrashLoopBackOff until each DataStore was manually re-reconciled. The hook was a one-shot `2.6.0 → 2.6.1` migration that became a permanent footgun once chart versioning moved to `0.0.0+<git-hash>` (always `< 2.6.1` per semver) and after the underlying `rotationPolicy: Always` issue was fixed in `47d81f70`. The hook is now removed entirely. ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2462, backport #2511)
* **fix(kamaji): increase memory limits and add startup probe**: The kamaji controller frequently entered CrashLoopBackOff due to OOMKills (exit 137) within ~2025 seconds of startup, with the readiness probe failing while the controller was still finishing initialization. Memory limit raised from 500Mi to 512Mi, request from 100Mi to 256Mi, and a 60-second startup probe (12 attempts × 5s periods) is added so the controller has room to boot before liveness/readiness probes engage. ([**@IvanHunters**](https://github.com/IvanHunters) in #2421, backport #2491)
## Build
* **build(linstor): include linstor-gui in root image build target**: The `linstor-gui` package (added in #2382) was never wired into the root `Makefile`'s `build:` target, so CI never built or published the image. `ghcr.io/cozystack/cozystack/linstor-gui` returned `NAME_UNKNOWN` and `values.yaml` stayed pinned to `tag: 2.3.0` without a digest. The missing build line is added so the next CI run publishes the image and the per-package `Makefile` digest-pins `values.yaml` automatically. ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2498, backport #2518)
## Contributors
Thanks to everyone who contributed to this patch release:
* [**@IvanHunters**](https://github.com/IvanHunters)
* [**@kvaps**](https://github.com/kvaps)
* [**@myasnikovdaniil**](https://github.com/myasnikovdaniil)
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.3.0...v1.3.1

54
go.mod
View file

@ -2,7 +2,7 @@
module github.com/cozystack/cozystack
go 1.25.0
go 1.26.2
require (
github.com/cozystack/cozystack-scheduler/pkg/apis v0.1.1
@ -36,12 +36,12 @@ require (
)
require (
cel.dev/expr v0.24.0 // indirect
cel.dev/expr v0.25.1 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/coreos/go-semver v0.3.1 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
@ -69,13 +69,13 @@ require (
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/moby/spdystream v0.5.0 // indirect
github.com/moby/spdystream v0.5.1 // 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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
@ -91,34 +91,34 @@ require (
go.etcd.io/etcd/api/v3 v3.6.4 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.6.4 // indirect
go.etcd.io/etcd/client/v3 v3.6.4 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.37.0 // indirect
go.opentelemetry.io/otel/sdk v1.37.0 // indirect
go.opentelemetry.io/otel/trace v1.37.0 // indirect
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect
go.opentelemetry.io/otel v1.43.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 // indirect
go.opentelemetry.io/otel/metric v1.43.0 // indirect
go.opentelemetry.io/otel/sdk v1.43.0 // indirect
go.opentelemetry.io/otel/trace v1.43.0 // indirect
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.42.0 // indirect
golang.org/x/crypto v0.50.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/net v0.45.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.36.0 // indirect
golang.org/x/term v0.35.0 // indirect
golang.org/x/text v0.29.0 // indirect
golang.org/x/net v0.52.0 // indirect
golang.org/x/oauth2 v0.35.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.43.0 // indirect
golang.org/x/term v0.42.0 // indirect
golang.org/x/text v0.36.0 // indirect
golang.org/x/time v0.12.0 // indirect
golang.org/x/tools v0.37.0 // indirect
golang.org/x/tools v0.43.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/grpc v1.73.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d // indirect
google.golang.org/grpc v1.80.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect

114
go.sum
View file

@ -1,5 +1,5 @@
cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=
cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
@ -10,8 +10,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
@ -100,8 +100,8 @@ github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 h1:FbSCl+KggFl+Ocym490i/E
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
@ -122,8 +122,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/moby/spdystream v0.5.1 h1:9sNYeYZUcci9R6/w7KDaFWEWeV4LStVG78Mpyq/Zm/Y=
github.com/moby/spdystream v0.5.1/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
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=
@ -153,8 +153,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
@ -202,28 +202,28 @@ go.etcd.io/etcd/server/v3 v3.6.4 h1:LsCA7CzjVt+8WGrdsnh6RhC0XqCsLkBly3ve5rTxMAU=
go.etcd.io/etcd/server/v3 v3.6.4/go.mod h1:aYCL/h43yiONOv0QIR82kH/2xZ7m+IWYjzRmyQfnCAg=
go.etcd.io/raft/v3 v3.6.0 h1:5NtvbDVYpnfZWcIHgGRk9DyzkBIXOi8j+DDp1IcnUWQ=
go.etcd.io/raft/v3 v3.6.0/go.mod h1:nLvLevg6+xrVtHUmVaTcTz603gQPHfh7kUAwV6YpfGo=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 h1:0Qx7VGBacMm9ZENQ7TnNObTYI4ShC+lHI16seduaxZo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0/go.mod h1:Sje3i3MjSPKTSPvVWCaL8ugBzJwik3u4smCjUeuupqg=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo=
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk=
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@ -237,8 +237,8 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@ -247,48 +247,50 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM=
golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY=
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d h1:wT2n40TBqFY6wiwazVK9/iTWbsQrgk5ZfCSVFLO9LQA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

View file

@ -1,145 +0,0 @@
#!/usr/bin/env bats
# -----------------------------------------------------------------------------
# Chart-wide invariant for packages/apps/kubernetes:
#
# Every Deployment in this chart that mounts <release>-admin-kubeconfig as a
# Secret volume MUST:
# - declare that volume optional: true (so kubelet does not FailedMount
# while Kamaji is still provisioning the Secret), AND
# - include the wait-for-kubeconfig init container (so the pod becomes
# Ready only after Kamaji publishes the Secret).
#
# The per-template unittests in packages/apps/kubernetes/tests/ lock in
# today's three Deployments (cluster-autoscaler, kccm, csi controller) by
# name. This invariant is stricter: any future Deployment added to this
# chart that mounts the same Secret but forgets the guard will fail here.
#
# Requires: helm, yq (mikefarah v4+), jq. All three are available on the
# project's CI runners and on the maintainer workstation.
# -----------------------------------------------------------------------------
@test "every Deployment mounting admin-kubeconfig has optional:true and wait-for-kubeconfig init" {
values_file="packages/apps/kubernetes/tests/values-ci.yaml"
[ -f "$values_file" ]
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
helm template invariant packages/apps/kubernetes \
--namespace tenant-root \
--values "$values_file" \
2>/dev/null > "$tmp/rendered.yaml"
# yq streams one JSON object per input document. jq -s slurps the stream
# into an array so we can treat all Deployments as a single collection.
yq --output-format=json eval-all '.' "$tmp/rendered.yaml" \
| jq -s --raw-output '
map(select(.kind == "Deployment")) |
map({
name: .metadata.name,
volumes: (.spec.template.spec.volumes // []),
initNames: ((.spec.template.spec.initContainers // []) | map(.name)),
}) |
map(
.name as $n |
.initNames as $ins |
(.volumes[] | select(.secret.secretName | test("-admin-kubeconfig$")?))
| {
name: $n,
optional: (.secret.optional == true),
hasInit: ($ins | index("wait-for-kubeconfig") != null),
}
)
' > "$tmp/summary.json"
# At least one Deployment must match; if a refactor removes every
# admin-kubeconfig volume from this chart, the test must be updated
# deliberately rather than silently passing.
matched=$(jq 'length' "$tmp/summary.json")
[ "$matched" -ge 1 ]
offenders=$(jq --raw-output '.[] | select(.optional != true or .hasInit != true) | .name' "$tmp/summary.json")
if [ -n "$offenders" ]; then
echo "Deployments mounting *-admin-kubeconfig without optional:true + wait-for-kubeconfig init:" >&2
echo "$offenders" >&2
echo "Full summary:" >&2
cat "$tmp/summary.json" >&2
exit 1
fi
echo "Invariant holds for $matched Deployment(s)"
}
@test "chart emits zero admin-kubeconfig Deployments when tenant has no etcd DataStore" {
# Without a DataStore (parent Tenant has not populated _namespace.etcd yet)
# the control-plane-side Deployments must NOT render at all. If they did,
# the wait-for-kubeconfig init would CrashLoopBackOff indefinitely - there
# would be no KamajiControlPlane to provision the Secret - consuming the
# HelmRelease wait budget and triggering exactly the remediation cycle the
# rest of this chart tries to avoid. This test renders the whole chart
# with etcd empty and asserts no Deployment references the admin-kubeconfig
# Secret.
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
helm template invariant packages/apps/kubernetes \
--namespace tenant-root \
--values packages/apps/kubernetes/tests/values-ci-no-etcd.yaml \
2>/dev/null > "$tmp/rendered.yaml"
matched=$(
yq --output-format=json eval-all '.' "$tmp/rendered.yaml" \
| jq -s '
map(select(.kind == "Deployment")) |
map(select(
(.spec.template.spec.volumes // [])
| any(.secret.secretName | test("-admin-kubeconfig$")?)
)) |
length
'
)
if [ "$matched" -ne 0 ]; then
echo "Expected zero Deployments mounting *-admin-kubeconfig when etcd is empty, got $matched:" >&2
yq --output-format=json eval-all '.' "$tmp/rendered.yaml" \
| jq -s 'map(select(.kind == "Deployment") | .metadata.name)' >&2
exit 1
fi
echo "No admin-kubeconfig Deployments rendered for empty etcd (as expected)"
}
@test "chart emits zero admin-kubeconfig HelmReleases when tenant has no etcd DataStore" {
# Same principle as the Deployment variant above, extended to every child
# HelmRelease (cilium, coredns, csi, cert-manager, ...). They reference
# *-admin-kubeconfig via kubeConfig.secretRef and would otherwise sit in
# NotReady forever on an etcd-less tenant, polluting the HelmRelease list
# the operator sees and contradicting the "awaiting-etcd beacon only"
# contract of the soft-skip path.
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
helm template invariant packages/apps/kubernetes \
--namespace tenant-root \
--values packages/apps/kubernetes/tests/values-ci-no-etcd.yaml \
2>/dev/null > "$tmp/rendered.yaml"
matched=$(
yq --output-format=json eval-all '.' "$tmp/rendered.yaml" \
| jq -s '
map(select(.kind == "HelmRelease")) |
map(select(.spec.kubeConfig.secretRef.name | test("-admin-kubeconfig$")?)) |
length
'
)
if [ "$matched" -ne 0 ]; then
echo "Expected zero HelmReleases referencing *-admin-kubeconfig when etcd is empty, got $matched:" >&2
yq --output-format=json eval-all '.' "$tmp/rendered.yaml" \
| jq -s 'map(select(.kind == "HelmRelease") | .metadata.name)' >&2
exit 1
fi
echo "No admin-kubeconfig HelmReleases rendered for empty etcd (as expected)"
}

View file

@ -1,206 +0,0 @@
#!/usr/bin/env bats
# -----------------------------------------------------------------------------
# Cross-validation between GPU recording rules, the dashboards that consume
# them, and the DCGM Exporter metric set the cluster actually scrapes. Catches:
#
# 1. dangling references — a dashboard query mentions a recording rule name
# that doesn't exist in gpu-recording.rules.yaml. This is the bug the
# pre-merge review caught: gpu-efficiency.json shipped panels keyed on
# pod:tensor_saturation:avg5m without the rule being defined, so the
# panel showed "No data" everywhere.
#
# 2. typos in rule names — same bug class, manifested as a single-character
# difference between rule and reference.
#
# 3. undeclared DCGM metrics — a dashboard query or recording rule mentions
# a DCGM_FI_* metric that is neither in the upstream default CSV nor in
# the project's custom CSV (dcgm-custom-metrics.yaml), meaning DCGM
# Exporter would never emit it and the panel silently shows "No data".
# Example regression: gpu-fleet.json shipped a TDP panel referencing
# DCGM_FI_DEV_POWER_MGMT_LIMIT before the custom CSV declared it.
#
# Scope: only dashboards listed in packages/system/monitoring/dashboards-infra.list
# under the "gpu/" prefix (i.e. shipped to production), not every JSON file in
# dashboards/gpu/. Untracked drafts stay out of scope on purpose — adding one
# to dashboards-infra.list is what brings it under the test.
#
# Reverse direction (rule defined but never consumed) is intentionally NOT
# enforced: some rules exist for ad-hoc PromQL or upcoming dashboards. Treat
# unused rules as an editorial concern, not a regression.
#
# Title syntax constraints from cozytest.sh's awk parser:
# - Titles delimited by ASCII double quotes; embedded quotes truncate.
# - Only [A-Za-z0-9] from the title survives into the function name; titles
# differing only in punctuation collapse to the same function.
#
# Run with: hack/cozytest.sh hack/check-gpu-recording-rules.bats
# -----------------------------------------------------------------------------
REPO_ROOT="$(cd "$(dirname "${BATS_TEST_FILENAME:-$0}")/.." && pwd)"
RULES_FILE="$REPO_ROOT/packages/system/monitoring-agents/alerts/gpu-recording.rules.yaml"
DASHBOARDS_LIST="$REPO_ROOT/packages/system/monitoring/dashboards-infra.list"
DASHBOARDS_DIR="$REPO_ROOT/dashboards"
DCGM_DEFAULT_CSV="$REPO_ROOT/hack/dcgm-default-counters.csv"
DCGM_CUSTOM_CSV="$REPO_ROOT/packages/system/gpu-operator/examples/dcgm-custom-metrics.yaml"
# Extract the set of "- record: NAME" entries from the rules YAML.
# Outputs one rule name per line, sorted and deduplicated.
extract_rules() {
awk '/^[[:space:]]*-[[:space:]]*record:[[:space:]]/ {
sub(/^[[:space:]]*-[[:space:]]*record:[[:space:]]*/, "")
sub(/[[:space:]]*$/, "")
print
}' "$RULES_FILE" | sort -u
}
# Extract the set of recording-rule references from a dashboard JSON.
# A recording-rule reference is matched by the pattern
# <segment>:<segment>(:<segment>)+
# where each <segment> is [a-z0-9_]. Raw DCGM metrics (DCGM_FI_*),
# kube-state-metrics (kube_*) and similar uppercase / single-word metric
# names do not match because the leading segment must be lowercase and the
# whole expression must contain at least two ':' characters.
extract_refs() {
json_file=$1
# Prometheus convention allows 2-segment rule names (level:metric); this
# regex is tuned to the 3+ segment convention used in this repo
# (level:metric:op — e.g. cluster:gpu_count:total). Update if future
# rules use 2 segments, otherwise they will be silently skipped.
grep -hoE '[a-z][a-z0-9_]*:[a-z0-9_]+:[a-z0-9_]+' "$json_file" | sort -u
}
# Resolve "gpu/foo" -> "$DASHBOARDS_DIR/gpu/foo.json"
list_tracked_gpu_dashboards() {
awk '/^gpu\// { print $0 ".json" }' "$DASHBOARDS_LIST"
}
# Extract the set of DCGM_FI_* metric names declared in a CSV file. Handles
# both the upstream-style default CSV (unindented) and the ConfigMap-style
# custom CSV (YAML-indented). A declaration line starts — after any leading
# whitespace — with "DCGM_FI_<NAME>," ; comment lines begin with "#" and are
# skipped. Uses POSIX awk's match()+RSTART/RLENGTH so no GNU extensions
# are required.
extract_csv_metrics() {
file=$1
awk '
{
line = $0
sub(/^[[:space:]]+/, "", line)
if (line ~ /^#/) next
if (match(line, /^DCGM_FI_[A-Z0-9_]+/)) {
print substr(line, RSTART, RLENGTH)
}
}
' "$file" | sort -u
}
# Extract the set of DCGM_FI_* metric references from a text file (dashboard
# JSON or rules YAML). A DCGM metric name has at least two underscore-delimited
# segments after the "DCGM_FI_" prefix (e.g. DCGM_FI_DEV_GPU_UTIL, DCGM_FI_PROF_
# PIPE_TENSOR_ACTIVE, DCGM_FI_DRIVER_VERSION). Requiring two segments keeps
# the matcher from latching onto glob stubs like "DCGM_FI_DEV_*_VIOLATION" that
# appear in comments.
extract_dcgm_refs() {
file=$1
grep -hoE 'DCGM_FI_[A-Z0-9]+(_[A-Z0-9]+)+' "$file" | sort -u
}
@test "every recording rule reference in tracked GPU dashboards has a matching record" {
TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT
extract_rules > "$TMP/rules.txt"
[ -s "$TMP/rules.txt" ] || { echo "no recording rules extracted from $RULES_FILE" >&2; exit 1; }
list_tracked_gpu_dashboards > "$TMP/dashboards.txt"
[ -s "$TMP/dashboards.txt" ] || { echo "no gpu/* dashboards listed in $DASHBOARDS_LIST" >&2; exit 1; }
failed=0
while IFS= read -r dashboard_rel; do
dashboard="$DASHBOARDS_DIR/$dashboard_rel"
if [ ! -f "$dashboard" ]; then
echo "ERROR: dashboard listed but file missing: $dashboard" >&2
failed=1
continue
fi
extract_refs "$dashboard" > "$TMP/refs.txt"
# comm -23: lines unique to refs.txt (referenced but not defined)
# Both inputs must be sorted; extract_* helpers already sort.
comm -23 "$TMP/refs.txt" "$TMP/rules.txt" > "$TMP/missing.txt"
if [ -s "$TMP/missing.txt" ]; then
echo "ERROR: $dashboard_rel references undefined recording rules:" >&2
sed 's/^/ - /' "$TMP/missing.txt" >&2
failed=1
fi
done < "$TMP/dashboards.txt"
[ "$failed" -eq 0 ]
}
@test "every DCGM metric referenced in tracked dashboards and rules is declared" {
TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT
[ -f "$DCGM_DEFAULT_CSV" ] || { echo "missing $DCGM_DEFAULT_CSV" >&2; exit 1; }
[ -f "$DCGM_CUSTOM_CSV" ] || { echo "missing $DCGM_CUSTOM_CSV" >&2; exit 1; }
{
extract_csv_metrics "$DCGM_DEFAULT_CSV"
extract_csv_metrics "$DCGM_CUSTOM_CSV"
} | sort -u > "$TMP/declared.txt"
[ -s "$TMP/declared.txt" ] || { echo "no DCGM metrics extracted from CSVs" >&2; exit 1; }
list_tracked_gpu_dashboards > "$TMP/dashboards.txt"
[ -s "$TMP/dashboards.txt" ] || { echo "no gpu/* dashboards listed in $DASHBOARDS_LIST" >&2; exit 1; }
failed=0
# Dashboard coverage — every dashboard's DCGM references must resolve.
while IFS= read -r dashboard_rel; do
dashboard="$DASHBOARDS_DIR/$dashboard_rel"
[ -f "$dashboard" ] || continue # handled by the existence test
extract_dcgm_refs "$dashboard" > "$TMP/refs.txt"
[ -s "$TMP/refs.txt" ] || continue # dashboard relies entirely on recording rules
comm -23 "$TMP/refs.txt" "$TMP/declared.txt" > "$TMP/missing.txt"
if [ -s "$TMP/missing.txt" ]; then
echo "ERROR: $dashboard_rel references DCGM metrics not declared in any CSV:" >&2
sed 's/^/ - /' "$TMP/missing.txt" >&2
failed=1
fi
done < "$TMP/dashboards.txt"
# Rules coverage — recording rules consume DCGM directly, so their set
# must be declared too, otherwise derived series on every dashboard
# collapse to empty.
extract_dcgm_refs "$RULES_FILE" > "$TMP/rule-refs.txt"
if [ -s "$TMP/rule-refs.txt" ]; then
comm -23 "$TMP/rule-refs.txt" "$TMP/declared.txt" > "$TMP/rule-missing.txt"
if [ -s "$TMP/rule-missing.txt" ]; then
echo "ERROR: gpu-recording.rules.yaml references DCGM metrics not declared in any CSV:" >&2
sed 's/^/ - /' "$TMP/rule-missing.txt" >&2
failed=1
fi
fi
[ "$failed" -eq 0 ]
}
@test "every tracked GPU dashboard listed in dashboards-infra.list exists on disk" {
TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT
list_tracked_gpu_dashboards > "$TMP/dashboards.txt"
[ -s "$TMP/dashboards.txt" ] || { echo "no gpu/* dashboards listed in $DASHBOARDS_LIST" >&2; exit 1; }
failed=0
while IFS= read -r dashboard_rel; do
dashboard="$DASHBOARDS_DIR/$dashboard_rel"
if [ ! -f "$dashboard" ]; then
echo "ERROR: $dashboard_rel listed in dashboards-infra.list but $dashboard does not exist" >&2
failed=1
fi
done < "$TMP/dashboards.txt"
[ "$failed" -eq 0 ]
}

View file

@ -1,104 +0,0 @@
# Snapshot of the upstream DCGM Exporter default-counters.csv used as a
# fixture by hack/check-gpu-recording-rules.bats. The test verifies that
# every DCGM_FI_* metric referenced by a tracked GPU dashboard is either
# declared here (upstream defaults) or in
# packages/system/gpu-operator/examples/dcgm-custom-metrics.yaml
# (the project's custom CSV).
#
# Source: https://github.com/NVIDIA/dcgm-exporter/blob/4.1.1-4.0.4/etc/default-counters.csv
# Pinned to the DCGM Exporter image tag shipped by the gpu-operator
# chart under packages/system/gpu-operator/charts/gpu-operator/values.yaml
# (dcgmExporter.version = 4.1.1-4.0.4-ubuntu22.04). When that image is
# bumped, refresh this file from the matching tag in the NVIDIA/dcgm-exporter
# repo and re-run `./hack/cozytest.sh hack/check-gpu-recording-rules.bats`.
# Format
# If line starts with a '#' it is considered a comment
# DCGM FIELD, Prometheus metric type, help message
# Clocks
DCGM_FI_DEV_SM_CLOCK, gauge, SM clock frequency (in MHz).
DCGM_FI_DEV_MEM_CLOCK, gauge, Memory clock frequency (in MHz).
# Temperature
DCGM_FI_DEV_MEMORY_TEMP, gauge, Memory temperature (in C).
DCGM_FI_DEV_GPU_TEMP, gauge, GPU temperature (in C).
# Power
DCGM_FI_DEV_POWER_USAGE, gauge, Power draw (in W).
DCGM_FI_DEV_TOTAL_ENERGY_CONSUMPTION, counter, Total energy consumption since boot (in mJ).
# PCIE
# DCGM_FI_PROF_PCIE_TX_BYTES, counter, Total number of bytes transmitted through PCIe TX via NVML.
# DCGM_FI_PROF_PCIE_RX_BYTES, counter, Total number of bytes received through PCIe RX via NVML.
DCGM_FI_DEV_PCIE_REPLAY_COUNTER, counter, Total number of PCIe retries.
# Utilization (the sample period varies depending on the product)
DCGM_FI_DEV_GPU_UTIL, gauge, GPU utilization (in %).
DCGM_FI_DEV_MEM_COPY_UTIL, gauge, Memory utilization (in %).
DCGM_FI_DEV_ENC_UTIL, gauge, Encoder utilization (in %).
DCGM_FI_DEV_DEC_UTIL , gauge, Decoder utilization (in %).
# Errors and violations
DCGM_FI_DEV_XID_ERRORS, gauge, Value of the last XID error encountered.
# DCGM_FI_DEV_POWER_VIOLATION, counter, Throttling duration due to power constraints (in us).
# DCGM_FI_DEV_THERMAL_VIOLATION, counter, Throttling duration due to thermal constraints (in us).
# DCGM_FI_DEV_SYNC_BOOST_VIOLATION, counter, Throttling duration due to sync-boost constraints (in us).
# DCGM_FI_DEV_BOARD_LIMIT_VIOLATION, counter, Throttling duration due to board limit constraints (in us).
# DCGM_FI_DEV_LOW_UTIL_VIOLATION, counter, Throttling duration due to low utilization (in us).
# DCGM_FI_DEV_RELIABILITY_VIOLATION, counter, Throttling duration due to reliability constraints (in us).
# Memory usage
DCGM_FI_DEV_FB_FREE, gauge, Framebuffer memory free (in MiB).
DCGM_FI_DEV_FB_USED, gauge, Framebuffer memory used (in MiB).
# ECC
# DCGM_FI_DEV_ECC_SBE_VOL_TOTAL, counter, Total number of single-bit volatile ECC errors.
# DCGM_FI_DEV_ECC_DBE_VOL_TOTAL, counter, Total number of double-bit volatile ECC errors.
# DCGM_FI_DEV_ECC_SBE_AGG_TOTAL, counter, Total number of single-bit persistent ECC errors.
# DCGM_FI_DEV_ECC_DBE_AGG_TOTAL, counter, Total number of double-bit persistent ECC errors.
# Retired pages
# DCGM_FI_DEV_RETIRED_SBE, counter, Total number of retired pages due to single-bit errors.
# DCGM_FI_DEV_RETIRED_DBE, counter, Total number of retired pages due to double-bit errors.
# DCGM_FI_DEV_RETIRED_PENDING, counter, Total number of pages pending retirement.
# NVLink
# DCGM_FI_DEV_NVLINK_CRC_FLIT_ERROR_COUNT_TOTAL, counter, Total number of NVLink flow-control CRC errors.
# DCGM_FI_DEV_NVLINK_CRC_DATA_ERROR_COUNT_TOTAL, counter, Total number of NVLink data CRC errors.
# DCGM_FI_DEV_NVLINK_REPLAY_ERROR_COUNT_TOTAL, counter, Total number of NVLink retries.
# DCGM_FI_DEV_NVLINK_RECOVERY_ERROR_COUNT_TOTAL, counter, Total number of NVLink recovery errors.
DCGM_FI_DEV_NVLINK_BANDWIDTH_TOTAL, counter, Total number of NVLink bandwidth counters for all lanes.
# DCGM_FI_DEV_NVLINK_BANDWIDTH_L0, counter, The number of bytes of active NVLink rx or tx data including both header and payload.
# VGPU License status
DCGM_FI_DEV_VGPU_LICENSE_STATUS, gauge, vGPU License status
# Remapped rows
DCGM_FI_DEV_UNCORRECTABLE_REMAPPED_ROWS, counter, Number of remapped rows for uncorrectable errors
DCGM_FI_DEV_CORRECTABLE_REMAPPED_ROWS, counter, Number of remapped rows for correctable errors
DCGM_FI_DEV_ROW_REMAP_FAILURE, gauge, Whether remapping of rows has failed
# Static configuration information. These appear as labels on the other metrics
DCGM_FI_DRIVER_VERSION, label, Driver Version
# DCGM_FI_NVML_VERSION, label, NVML Version
# DCGM_FI_DEV_BRAND, label, Device Brand
# DCGM_FI_DEV_SERIAL, label, Device Serial Number
# DCGM_FI_DEV_OEM_INFOROM_VER, label, OEM inforom version
# DCGM_FI_DEV_ECC_INFOROM_VER, label, ECC inforom version
# DCGM_FI_DEV_POWER_INFOROM_VER, label, Power management object inforom version
# DCGM_FI_DEV_INFOROM_IMAGE_VER, label, Inforom image version
# DCGM_FI_DEV_VBIOS_VERSION, label, VBIOS version of the device
# Datacenter Profiling (DCP) metrics
# NOTE: supported on Nvidia datacenter Volta GPUs and newer
DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active.
# DCGM_FI_PROF_SM_ACTIVE, gauge, The ratio of cycles an SM has at least 1 warp assigned.
# DCGM_FI_PROF_SM_OCCUPANCY, gauge, The ratio of number of warps resident on an SM.
DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, Ratio of cycles the tensor (HMMA) pipe is active.
DCGM_FI_PROF_DRAM_ACTIVE, gauge, Ratio of cycles the device memory interface is active sending or receiving data.
# DCGM_FI_PROF_PIPE_FP64_ACTIVE, gauge, Ratio of cycles the fp64 pipes are active.
# DCGM_FI_PROF_PIPE_FP32_ACTIVE, gauge, Ratio of cycles the fp32 pipes are active.
# DCGM_FI_PROF_PIPE_FP16_ACTIVE, gauge, Ratio of cycles the fp16 pipes are active.
DCGM_FI_PROF_PCIE_TX_BYTES, gauge, The rate of data transmitted over the PCIe bus - including both protocol headers and data payloads - in bytes per second.
DCGM_FI_PROF_PCIE_RX_BYTES, gauge, The rate of data received over the PCIe bus - including both protocol headers and data payloads - in bytes per second.
Can't render this file because it has a wrong number of fields in line 12.

View file

@ -1,39 +0,0 @@
# Helpers for asserting that a Flux HelmRelease did not fall into an
# install/upgrade remediation cycle during an e2e run.
#
# Background: Flux helm-controller's ClearFailures() zeroes
# .status.installFailures / .status.upgradeFailures on every successful
# reconciliation (see the upstream ClearFailures method on
# HelmReleaseStatus). That makes those counters useless for a guard that
# runs after the HelmRelease has reached Ready - the values are always 0.
#
# What survives a successful reconciliation is .status.history, a bounded
# list of release Snapshots. Each Snapshot carries a status field that
# tracks the Helm release state: deployed, superseded, failed, uninstalled,
# and so on. A remediation cycle leaves the footprint behind: a snapshot
# with status "uninstalled" (from install/upgrade remediation) or "failed"
# (Helm release failure that remediation then uninstalled). Those stay in
# history even after a subsequent successful reinstall.
#
# helmrelease_has_remediation_cycle takes a newline-delimited list of
# snapshot statuses (whatever the caller extracted via kubectl -o jsonpath
# or equivalent) and returns 0 (detected) when any entry is "failed" or
# "uninstalled", 1 otherwise. Empty input is treated as "no history yet,
# no cycle observed".
helmrelease_has_remediation_cycle() {
statuses="$1"
if [ -z "${statuses}" ]; then
return 1
fi
# printf + grep over the pipe, rather than a heredoc plus while read.
# printf %s treats the status string as a literal payload, so any stray
# $ in a future caller's input does not trigger shell expansion. grep
# returns 0 iff at least one line matches the allowlist, which is
# exactly the contract the caller wants, so we can return its exit
# status directly.
if printf '%s\n' "${statuses}" | grep --extended-regexp --quiet '^(failed|uninstalled)$'; then
return 0
fi
return 1
}

View file

@ -1,5 +1,3 @@
. hack/e2e-apps/remediation-guard.sh
run_kubernetes_test() {
local version_expr="$1"
local test_name="$2"
@ -322,35 +320,6 @@ EOF
done
kubectl wait hr kubernetes-${test_name}-ingress-nginx -n tenant-test --timeout=5m --for=condition=ready
# Guard: parent HelmRelease must not have entered an install/upgrade remediation cycle.
# A non-zero installFailures/upgradeFailures indicates the helm-wait budget expired while
# admin-kubeconfig was still being provisioned, which would trigger uninstall remediation
# and churn the Cluster CR.
# Flux helm-controller v2 retains per-revision release Snapshots in
# .status.history; each Snapshot's .status reflects the Helm release
# state (deployed/superseded/failed/uninstalled). A remediation cycle
# leaves a "failed" or "uninstalled" entry behind that survives a later
# successful reinstall, unlike the installFailures/upgradeFailures
# counters (which ClearFailures zeroes on every successful reconcile).
# The shape is pinned by hack/remediation-guard.bats; the upstream
# types are github.com/fluxcd/helm-controller/api v2 Snapshot.
history_statuses=$(kubectl get hr -n tenant-test "kubernetes-${test_name}" \
-ojsonpath='{range .status.history[*]}{.status}{"\n"}{end}')
# Always emit the raw value so a silent future-Flux field rename shows
# up as "empty history on a Ready HR" in CI logs rather than vanishing.
echo "Parent HelmRelease history statuses:"
printf '%s\n' "${history_statuses:-<empty>}"
if [ -z "${history_statuses}" ]; then
echo "Unexpected empty .status.history on a Ready HelmRelease - Flux API shape may have changed." >&2
kubectl -n tenant-test describe hr "kubernetes-${test_name}" >&2
exit 1
fi
if helmrelease_has_remediation_cycle "${history_statuses}"; then
echo "Parent HelmRelease entered remediation cycle." >&2
kubectl -n tenant-test describe hr "kubernetes-${test_name}" >&2
exit 1
fi
# Clean up
pkill -f "port-forward.*${port}:" 2>/dev/null || true
rm -f "tenantkubeconfig-${test_name}"

View file

@ -1,127 +0,0 @@
#!/usr/bin/env bats
# -----------------------------------------------------------------------------
# Unit tests for hack/e2e-apps/remediation-guard.sh
#
# helmrelease_has_remediation_cycle takes a newline-delimited list of
# HelmRelease history snapshot status values (deployed/superseded/failed/
# uninstalled/...) and returns 0 when any entry is "failed" or "uninstalled"
# (meaning flux helm-controller performed install/upgrade remediation).
#
# This is used by the e2e script after the HelmRelease reaches Ready. The
# failure/upgrade counters (.status.installFailures / .status.upgradeFailures)
# are useless there because flux's ClearFailures zeroes them on successful
# reconciliation; .status.history retains the snapshot trail.
#
# cozytest.sh's awk parser recognizes only @test blocks and a bare `}` on
# its own line; there is no bats `run` or `$status`. Assertions are
# expressed as direct shell tests that exit non-zero on failure.
#
# Run with: hack/cozytest.sh hack/remediation-guard.bats
# -----------------------------------------------------------------------------
@test "empty history returns not-detected" {
. hack/e2e-apps/remediation-guard.sh
if helmrelease_has_remediation_cycle ""; then
echo "expected not-detected for empty history" >&2
exit 1
fi
}
@test "single deployed snapshot returns not-detected" {
. hack/e2e-apps/remediation-guard.sh
if helmrelease_has_remediation_cycle "deployed"; then
echo "expected not-detected for deployed-only history" >&2
exit 1
fi
}
@test "deployed then superseded returns not-detected" {
. hack/e2e-apps/remediation-guard.sh
statuses=$(printf 'deployed\nsuperseded\n')
if helmrelease_has_remediation_cycle "${statuses}"; then
echo "expected not-detected for deployed+superseded history" >&2
exit 1
fi
}
@test "single failed snapshot returns detected" {
. hack/e2e-apps/remediation-guard.sh
if ! helmrelease_has_remediation_cycle "failed"; then
echo "expected detected when history contains failed snapshot" >&2
exit 1
fi
}
@test "single uninstalled snapshot returns detected" {
# The exact signature of the install-remediation race: the first install
# exceeded flux's wait budget, remediation uninstalled, the next retry
# eventually succeeded. History still carries the uninstalled snapshot.
. hack/e2e-apps/remediation-guard.sh
if ! helmrelease_has_remediation_cycle "uninstalled"; then
echo "expected detected when history contains uninstalled snapshot" >&2
exit 1
fi
}
@test "uninstalled then deployed still returns detected" {
. hack/e2e-apps/remediation-guard.sh
statuses=$(printf 'uninstalled\ndeployed\n')
if ! helmrelease_has_remediation_cycle "${statuses}"; then
echo "expected detected despite later successful deploy" >&2
exit 1
fi
}
@test "deployed then failed still returns detected" {
. hack/e2e-apps/remediation-guard.sh
statuses=$(printf 'deployed\nfailed\n')
if ! helmrelease_has_remediation_cycle "${statuses}"; then
echo "expected detected when any entry is failed" >&2
exit 1
fi
}
@test "status.history extraction pins HR v2 status.history shape" {
# Pins the Flux HelmRelease v2 .status.history[].status shape that
# run-kubernetes.sh relies on. If a future flux release renames the
# field, the jsonpath returns nothing, the guard reports no cycle,
# and real remediation loops slip past the e2e assertion. This test
# uses yq to read the exact path used in the e2e script; the upstream
# Snapshot type lives at
# github.com/fluxcd/helm-controller/api/v2.Snapshot (via go.mod).
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
cat > "$tmp/hr.yaml" <<'YAML'
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: kubernetes-test
namespace: tenant-test
status:
history:
- name: kubernetes-test
namespace: tenant-test
version: 1
status: uninstalled
- name: kubernetes-test
namespace: tenant-test
version: 2
status: deployed
YAML
# Default yq output is yaml scalar format, which for string values emits
# bare unquoted tokens - matching what kubectl -o jsonpath produces in
# e2e. Do not switch to JSON output here; that would quote the values
# and break the loop in helmrelease_has_remediation_cycle.
statuses=$(yq '.status.history[].status' "$tmp/hr.yaml")
[ -n "$statuses" ]
echo "$statuses" | grep --quiet '^uninstalled$'
. hack/e2e-apps/remediation-guard.sh
if ! helmrelease_has_remediation_cycle "$statuses"; then
echo "expected detected for pinned HR snippet with uninstalled + deployed history" >&2
exit 1
fi
}

View file

@ -118,19 +118,6 @@ spec:
ReleaseName is the name of the HelmRelease resource that will be created
If not specified, defaults to the component Name field
type: string
upgradeCRDs:
description: |-
UpgradeCRDs controls how CRDs from the chart's crds/ directory are
handled on HelmRelease upgrades. Maps to HelmRelease.Spec.Upgrade.CRDs.
Empty string (default) preserves the helm-controller default (Skip).
Use "CreateReplace" for operators that evolve their CRD set between
versions. Warning: CreateReplace overwrites CRDs and may cause data
loss if upstream drops fields from a CRD with live objects.
enum:
- Skip
- Create
- CreateReplace
type: string
type: object
libraries:
description: |-

View file

@ -45,16 +45,6 @@ const (
SecretCozystackValues = "cozystack-values"
)
// parseCRDPolicy maps ComponentInstall.UpgradeCRDs to a helmv2.CRDsPolicy.
// Empty / nil preserves the helm-controller default (Skip on upgrade);
// the CRD enum marker restricts the string to Skip/Create/CreateReplace.
func parseCRDPolicy(install *cozyv1alpha1.ComponentInstall) helmv2.CRDsPolicy {
if install == nil || install.UpgradeCRDs == "" {
return ""
}
return helmv2.CRDsPolicy(install.UpgradeCRDs)
}
// PackageReconciler reconciles Package resources
type PackageReconciler struct {
client.Client
@ -231,7 +221,6 @@ func (r *PackageReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
Remediation: &helmv2.UpgradeRemediation{
Retries: -1,
},
CRDs: parseCRDPolicy(component.Install),
},
},
}

View file

@ -1,138 +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 operator
import (
"encoding/json"
"os"
"path/filepath"
"testing"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
helmv2 "github.com/fluxcd/helm-controller/api/v2"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"sigs.k8s.io/yaml"
)
func TestParseCRDPolicy(t *testing.T) {
tests := []struct {
name string
install *cozyv1alpha1.ComponentInstall
want helmv2.CRDsPolicy
}{
{
name: "nil install leaves flux default",
install: nil,
want: "",
},
{
name: "empty upgradeCRDs leaves flux default",
install: &cozyv1alpha1.ComponentInstall{},
want: "",
},
{
name: "Skip is passed through",
install: &cozyv1alpha1.ComponentInstall{UpgradeCRDs: "Skip"},
want: helmv2.Skip,
},
{
name: "Create is passed through",
install: &cozyv1alpha1.ComponentInstall{UpgradeCRDs: "Create"},
want: helmv2.Create,
},
{
name: "CreateReplace is passed through",
install: &cozyv1alpha1.ComponentInstall{UpgradeCRDs: "CreateReplace"},
want: helmv2.CreateReplace,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got := parseCRDPolicy(tc.install)
if got != tc.want {
t.Errorf("parseCRDPolicy() = %q, want %q", got, tc.want)
}
})
}
}
// TestPackageSourceCRDHasUpgradeCRDsEnum guards the generated CRD schema: the
// invalid-value case from the spec is enforced at the API server via a
// kubebuilder enum marker, not in the reconciler. If someone drops the marker
// and forgets to regenerate, this test catches it.
func TestPackageSourceCRDHasUpgradeCRDsEnum(t *testing.T) {
path := filepath.Join("..", "crdinstall", "manifests", "cozystack.io_packagesources.yaml")
data, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read %s: %v", path, err)
}
var crd apiextensionsv1.CustomResourceDefinition
if err := yaml.Unmarshal(data, &crd); err != nil {
t.Fatalf("unmarshal CRD: %v", err)
}
var field *apiextensionsv1.JSONSchemaProps
for i := range crd.Spec.Versions {
v := &crd.Spec.Versions[i]
if v.Schema == nil || v.Schema.OpenAPIV3Schema == nil {
continue
}
spec, ok := v.Schema.OpenAPIV3Schema.Properties["spec"]
if !ok {
continue
}
variants, ok := spec.Properties["variants"]
if !ok || variants.Items == nil || variants.Items.Schema == nil {
continue
}
components, ok := variants.Items.Schema.Properties["components"]
if !ok || components.Items == nil || components.Items.Schema == nil {
continue
}
install, ok := components.Items.Schema.Properties["install"]
if !ok {
continue
}
f, ok := install.Properties["upgradeCRDs"]
if !ok {
continue
}
field = &f
break
}
if field == nil {
t.Fatal("upgradeCRDs field not found in PackageSource CRD schema")
}
got := map[string]bool{}
for _, e := range field.Enum {
var s string
if err := json.Unmarshal(e.Raw, &s); err != nil {
t.Fatalf("unmarshal enum value %q: %v", e.Raw, err)
}
got[s] = true
}
for _, want := range []string{"Skip", "Create", "CreateReplace"} {
if !got[want] {
t.Errorf("enum value %q missing from upgradeCRDs; got %v", want, got)
}
}
}

View file

@ -4,9 +4,6 @@ KUBERNETES_PKG_TAG = $(shell awk '$$1 == "version:" {print $$2}' Chart.yaml)
include ../../../hack/common-envs.mk
include ../../../hack/package.mk
test:
helm unittest .
generate:
cozyvalues-gen -m 'kubernetes' -v values.yaml -s values.schema.json -r README.md -g ../../../api/apps/v1alpha1/kubernetes/types.go
../../../hack/update-crd.sh
@ -70,4 +67,3 @@ image-cluster-autoscaler:
echo "$(REGISTRY)/cluster-autoscaler:$(call settag,$(KUBERNETES_PKG_TAG))@$$(yq e '."containerimage.digest"' images/cluster-autoscaler.json -o json -r)" \
> images/cluster-autoscaler.tag
rm -f images/cluster-autoscaler.json

View file

@ -128,9 +128,6 @@ See the reference for components utilized in this service:
| `addons.gpuOperator` | NVIDIA GPU Operator. | `object` | `{}` |
| `addons.gpuOperator.enabled` | Enable GPU Operator. | `bool` | `false` |
| `addons.gpuOperator.valuesOverride` | Custom Helm values overrides. | `object` | `{}` |
| `addons.hami` | HAMi GPU virtualization middleware. | `object` | `{}` |
| `addons.hami.enabled` | Enable HAMi (requires GPU Operator). | `bool` | `false` |
| `addons.hami.valuesOverride` | Custom Helm values overrides. | `object` | `{}` |
| `addons.fluxcd` | FluxCD GitOps operator. | `object` | `{}` |
| `addons.fluxcd.enabled` | Enable FluxCD. | `bool` | `false` |
| `addons.fluxcd.valuesOverride` | Custom Helm values overrides. | `object` | `{}` |
@ -148,33 +145,31 @@ See the reference for components utilized in this service:
### Kubernetes Control Plane Configuration
| Name | Description | Type | Value |
| --------------------------------------------------- | --------------------------------------------------------------------------------------------- | ---------- | ------- |
| `controlPlane` | Kubernetes control-plane configuration. | `object` | `{}` |
| `controlPlane.replicas` | Number of control-plane replicas. | `int` | `2` |
| `controlPlane.apiServer` | API Server configuration. | `object` | `{}` |
| `controlPlane.apiServer.resources` | CPU and memory resources for API Server. | `object` | `{}` |
| `controlPlane.apiServer.resources.cpu` | CPU available. | `quantity` | `""` |
| `controlPlane.apiServer.resources.memory` | Memory (RAM) available. | `quantity` | `""` |
| `controlPlane.apiServer.resourcesPreset` | Preset if `resources` omitted. | `string` | `large` |
| `controlPlane.controllerManager` | Controller Manager configuration. | `object` | `{}` |
| `controlPlane.controllerManager.resources` | CPU and memory resources for Controller Manager. | `object` | `{}` |
| `controlPlane.controllerManager.resources.cpu` | CPU available. | `quantity` | `""` |
| `controlPlane.controllerManager.resources.memory` | Memory (RAM) available. | `quantity` | `""` |
| `controlPlane.controllerManager.resourcesPreset` | Preset if `resources` omitted. | `string` | `micro` |
| `controlPlane.scheduler` | Scheduler configuration. | `object` | `{}` |
| `controlPlane.scheduler.resources` | CPU and memory resources for Scheduler. | `object` | `{}` |
| `controlPlane.scheduler.resources.cpu` | CPU available. | `quantity` | `""` |
| `controlPlane.scheduler.resources.memory` | Memory (RAM) available. | `quantity` | `""` |
| `controlPlane.scheduler.resourcesPreset` | Preset if `resources` omitted. | `string` | `micro` |
| `controlPlane.konnectivity` | Konnectivity configuration. | `object` | `{}` |
| `controlPlane.konnectivity.server` | Konnectivity Server configuration. | `object` | `{}` |
| `controlPlane.konnectivity.server.resources` | CPU and memory resources for Konnectivity. | `object` | `{}` |
| `controlPlane.konnectivity.server.resources.cpu` | CPU available. | `quantity` | `""` |
| `controlPlane.konnectivity.server.resources.memory` | Memory (RAM) available. | `quantity` | `""` |
| `controlPlane.konnectivity.server.resourcesPreset` | Preset if `resources` omitted. | `string` | `micro` |
| `images` | Optional image overrides for air-gapped or rate-limited registries. | `object` | `{}` |
| `images.waitForKubeconfig` | Image used by the wait-for-kubeconfig init container. Empty falls back to images/busybox.tag. | `string` | `""` |
| Name | Description | Type | Value |
| --------------------------------------------------- | ------------------------------------------------ | ---------- | ------- |
| `controlPlane` | Kubernetes control-plane configuration. | `object` | `{}` |
| `controlPlane.replicas` | Number of control-plane replicas. | `int` | `2` |
| `controlPlane.apiServer` | API Server configuration. | `object` | `{}` |
| `controlPlane.apiServer.resources` | CPU and memory resources for API Server. | `object` | `{}` |
| `controlPlane.apiServer.resources.cpu` | CPU available. | `quantity` | `""` |
| `controlPlane.apiServer.resources.memory` | Memory (RAM) available. | `quantity` | `""` |
| `controlPlane.apiServer.resourcesPreset` | Preset if `resources` omitted. | `string` | `large` |
| `controlPlane.controllerManager` | Controller Manager configuration. | `object` | `{}` |
| `controlPlane.controllerManager.resources` | CPU and memory resources for Controller Manager. | `object` | `{}` |
| `controlPlane.controllerManager.resources.cpu` | CPU available. | `quantity` | `""` |
| `controlPlane.controllerManager.resources.memory` | Memory (RAM) available. | `quantity` | `""` |
| `controlPlane.controllerManager.resourcesPreset` | Preset if `resources` omitted. | `string` | `micro` |
| `controlPlane.scheduler` | Scheduler configuration. | `object` | `{}` |
| `controlPlane.scheduler.resources` | CPU and memory resources for Scheduler. | `object` | `{}` |
| `controlPlane.scheduler.resources.cpu` | CPU available. | `quantity` | `""` |
| `controlPlane.scheduler.resources.memory` | Memory (RAM) available. | `quantity` | `""` |
| `controlPlane.scheduler.resourcesPreset` | Preset if `resources` omitted. | `string` | `micro` |
| `controlPlane.konnectivity` | Konnectivity configuration. | `object` | `{}` |
| `controlPlane.konnectivity.server` | Konnectivity Server configuration. | `object` | `{}` |
| `controlPlane.konnectivity.server.resources` | CPU and memory resources for Konnectivity. | `object` | `{}` |
| `controlPlane.konnectivity.server.resources.cpu` | CPU available. | `quantity` | `""` |
| `controlPlane.konnectivity.server.resources.memory` | Memory (RAM) available. | `quantity` | `""` |
| `controlPlane.konnectivity.server.resourcesPreset` | Preset if `resources` omitted. | `string` | `micro` |
## Parameter examples and reference

View file

@ -1 +0,0 @@
docker.io/library/busybox:1.37.0@sha256:1487d0af5f52b4ba31c7e465126ee2123fe3f2305d638e7827681e7cf6c83d5e

View file

@ -1,4 +1,4 @@
ARG builder_image=docker.io/library/golang:1.22.5
ARG builder_image=docker.io/library/golang:1.26
FROM ${builder_image} AS builder
ARG TARGETOS

View file

@ -1,101 +1,95 @@
module cozystack.io/kubevirt-csi-driver
go 1.22.0
toolchain go1.22.5
go 1.26.2
require (
github.com/container-storage-interface/spec v1.10.0
google.golang.org/grpc v1.65.0
google.golang.org/grpc v1.80.0
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.31.4
k8s.io/apimachinery v0.31.4
k8s.io/api v0.36.0
k8s.io/apimachinery v0.36.0
k8s.io/client-go v12.0.0+incompatible
k8s.io/klog/v2 v2.130.1
k8s.io/mount-utils v0.33.1
k8s.io/klog/v2 v2.140.0
k8s.io/mount-utils v0.36.0
kubevirt.io/containerized-data-importer-api v1.59.0
kubevirt.io/csi-driver v0.0.0-20250702202414-a8d6605bc999
kubevirt.io/csi-driver v0.0.0-20260424143118-bee6c348c004
)
require (
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.4 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/gnostic-models v0.7.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/imdario/mergo v0.3.15 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kubernetes-csi/csi-lib-utils v0.18.1 // indirect
github.com/kubernetes-csi/external-snapshotter/client/v6 v6.3.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/moby/sys/mountinfo v0.7.1 // indirect
github.com/moby/sys/mountinfo v0.7.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/opencontainers/runc v1.1.13 // indirect
github.com/opencontainers/runtime-spec v1.0.3-0.20220909204839-494a5a6aca78 // indirect
github.com/openshift/api v0.0.0-20230503133300-8bbcb7ca7183 // indirect
github.com/openshift/custom-resource-status v1.1.2 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/spf13/pflag v1.0.9 // indirect
github.com/x448/float16 v0.8.4 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/term v0.27.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.3.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
google.golang.org/protobuf v1.34.2 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/net v0.53.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sys v0.43.0 // indirect
golang.org/x/term v0.42.0 // indirect
golang.org/x/text v0.36.0 // indirect
golang.org/x/time v0.14.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.26.4 // indirect
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect
kubevirt.io/api v1.2.2 // indirect
kubevirt.io/controller-lifecycle-operator-sdk/api v0.0.0-20220329064328-f3cc58c6ed90 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.4.0 // 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.2 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
replace (
k8s.io/api => k8s.io/api v0.31.4
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.31.4
k8s.io/apimachinery => k8s.io/apimachinery v0.31.4
k8s.io/apiserver => k8s.io/apiserver v0.31.4
k8s.io/cli-runtime => k8s.io/cli-runtime v0.31.4
k8s.io/client-go => k8s.io/client-go v0.31.4
k8s.io/cloud-provider => k8s.io/cloud-provider v0.31.4
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.31.4
k8s.io/code-generator => k8s.io/code-generator v0.31.4
k8s.io/component-base => k8s.io/component-base v0.31.4
k8s.io/component-helpers => k8s.io/component-helpers v0.31.4
k8s.io/controller-manager => k8s.io/controller-manager v0.31.4
k8s.io/cri-api => k8s.io/cri-api v0.31.4
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.31.4
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.31.4
k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.31.4
k8s.io/kube-proxy => k8s.io/kube-proxy v0.31.4
k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.31.4
k8s.io/kubectl => k8s.io/kubectl v0.31.4
k8s.io/kubelet => k8s.io/kubelet v0.31.4
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.31.4
k8s.io/metrics => k8s.io/metrics v0.31.4
k8s.io/mount-utils => k8s.io/mount-utils v0.31.4
k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.31.4
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.31.4
kubevirt.io/csi-driver => github.com/kubevirt/csi-driver v0.0.0-20250702202414-a8d6605bc999
k8s.io/api => k8s.io/api v0.36.0
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.36.0
k8s.io/apimachinery => k8s.io/apimachinery v0.36.0
k8s.io/apiserver => k8s.io/apiserver v0.36.0
k8s.io/cli-runtime => k8s.io/cli-runtime v0.36.0
k8s.io/client-go => k8s.io/client-go v0.36.0
k8s.io/cloud-provider => k8s.io/cloud-provider v0.36.0
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.36.0
k8s.io/code-generator => k8s.io/code-generator v0.36.0
k8s.io/component-base => k8s.io/component-base v0.36.0
k8s.io/component-helpers => k8s.io/component-helpers v0.36.0
k8s.io/controller-manager => k8s.io/controller-manager v0.36.0
k8s.io/cri-api => k8s.io/cri-api v0.36.0
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.36.0
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.36.0
k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.36.0
k8s.io/kube-proxy => k8s.io/kube-proxy v0.36.0
k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.36.0
k8s.io/kubectl => k8s.io/kubectl v0.36.0
k8s.io/kubelet => k8s.io/kubelet v0.36.0
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.36.0
k8s.io/metrics => k8s.io/metrics v0.36.0
k8s.io/mount-utils => k8s.io/mount-utils v0.36.0
k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.36.0
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.36.0
kubevirt.io/csi-driver => github.com/kubevirt/csi-driver v0.0.0-20260424143118-bee6c348c004
)

View file

@ -1,21 +1,16 @@
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs=
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/container-storage-interface/spec v1.10.0 h1:YkzWPV39x+ZMTa6Ax2czJLLwpryrQ+dPesB34mrRMXA=
github.com/container-storage-interface/spec v1.10.0/go.mod h1:DtUvaQszPml1YJfIK7c00mlv6/g4wNMLanLgiUbKFRI=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -24,28 +19,30 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.15.0+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=
github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
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/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
@ -54,20 +51,12 @@ github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
@ -80,25 +69,21 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/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/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM=
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -108,9 +93,6 @@ github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2c
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM=
github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
@ -131,24 +113,24 @@ github.com/kubernetes-csi/csi-lib-utils v0.18.1 h1:vpg1kbQ6lFVCz7mY71zcqVE7W0GAQ
github.com/kubernetes-csi/csi-lib-utils v0.18.1/go.mod h1:PIcn27zmbY0KBue4JDdZVfDF56tjcS3jKroZPi+pMoY=
github.com/kubernetes-csi/external-snapshotter/client/v6 v6.3.0 h1:qS4r4ljINLWKJ9m9Ge3Q3sGZ/eIoDVDT2RhAdQFHb1k=
github.com/kubernetes-csi/external-snapshotter/client/v6 v6.3.0/go.mod h1:oGXx2XTEzs9ikW2V6IC1dD8trgjRsS/Mvc2JRiC618Y=
github.com/kubevirt/csi-driver v0.0.0-20250702202414-a8d6605bc999 h1:nq11swNPIRarVZm/YTOWh9rbuEqivgpA1c4RUEFSL90=
github.com/kubevirt/csi-driver v0.0.0-20250702202414-a8d6605bc999/go.mod h1:Pzl14YlqPpoK/ZdS79tUSuHbC2L/NPqOIjFTN5kTmUg=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
github.com/kubevirt/csi-driver v0.0.0-20260424143118-bee6c348c004 h1:fEnd+1kIEuzpFJt9slCylHsgPmxyLFO8K83hrToMkWs=
github.com/kubevirt/csi-driver v0.0.0-20260424143118-bee6c348c004/go.mod h1:Pzl14YlqPpoK/ZdS79tUSuHbC2L/NPqOIjFTN5kTmUg=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/moby/sys/mountinfo v0.7.1 h1:/tTvQaSJRr2FshkhXiIpux6fQ2Zvc4j7tAhMTStAG2g=
github.com/moby/sys/mountinfo v0.7.1/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
github.com/moby/spdystream v0.5.1/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
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.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
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/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
@ -162,23 +144,6 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU=
github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0=
github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo=
github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw=
github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo=
github.com/onsi/ginkgo/v2 v2.8.1/go.mod h1:N1/NbDngAFcSLdyZ+/aYTYGSlq9qMCS/cNKGJjy+csc=
github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk=
github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo=
github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0=
github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc=
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
@ -186,62 +151,40 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo=
github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc=
github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM=
github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=
github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM=
github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw=
github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557cZ6Gw=
github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ=
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4=
github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ=
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY=
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
github.com/opencontainers/runc v1.1.13 h1:98S2srgG9vw0zWcDpFMn5TRrh8kLxa/5OFUstuUhmRs=
github.com/opencontainers/runc v1.1.13/go.mod h1:R016aXacfp/gwQBYw2FDGa9m+n6atbLWrYY8hNMT/sA=
github.com/opencontainers/runtime-spec v1.0.3-0.20220909204839-494a5a6aca78 h1:R5M2qXZiK/mWPMT4VldCOiSL9HIAMuxQZWdG0CSM5+4=
github.com/opencontainers/runtime-spec v1.0.3-0.20220909204839-494a5a6aca78/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/openshift/api v0.0.0-20230503133300-8bbcb7ca7183 h1:t/CahSnpqY46sQR01SoS+Jt0jtjgmhgE6lFmRnO4q70=
github.com/openshift/api v0.0.0-20230503133300-8bbcb7ca7183/go.mod h1:4VWG+W22wrB4HfBL88P40DxLEpSOaiBVxUnfalfJo9k=
github.com/openshift/custom-resource-status v1.1.2 h1:C3DL44LEbvlbItfd8mT5jWrqPfHnSOQoQf/sypqA6A4=
github.com/openshift/custom-resource-status v1.1.2/go.mod h1:DB/Mf2oTeiAmVVX1gN+NEqweonAPY0TKUwADizj8+ZA=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
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/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@ -249,39 +192,65 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c=
go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE=
go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ=
go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps=
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0=
go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
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=
go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU=
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -295,32 +264,28 @@ golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -328,11 +293,17 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -352,79 +323,78 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240208230135-b75ee8823808/go.mod h1:KG1lNk5ZFNssSZLrpVb4sMXKMpGwGXOxSG3rnu2gZQQ=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0=
golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b/go.mod h1:4ZwOYna0/zsOKwuR5X/m0QFOJpSZvAxFfkQT+Erd9D4=
golang.org/x/telemetry v0.0.0-20250807160809-1a19826ec488/go.mod h1:fGb/2+tgXXjhjHsTNdVEEMZNWA0quBnfrO+AfoDSAKw=
golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053/go.mod h1:+nZKN+XVh4LCiA9DV3ywrzN4gumyCnKjau3NGb9SGoE=
golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE=
golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ=
golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ=
golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2/go.mod h1:b7fPSJ0pKZ3ccUh8gnTONJxhn3c/PS6tyzQvyqw4iA8=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
@ -434,32 +404,37 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/tools v0.34.1-0.20250613162507-3f93fece84c7/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
golang.org/x/tools/go/expect v0.1.0-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -469,18 +444,17 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI=
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
@ -497,47 +471,54 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
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/api v0.31.4 h1:I2QNzitPVsPeLQvexMEsj945QumYraqv9m74isPDKhM=
k8s.io/api v0.31.4/go.mod h1:d+7vgXLvmcdT1BCo79VEgJxHHryww3V5np2OYTr6jdw=
k8s.io/apiextensions-apiserver v0.31.4 h1:FxbqzSvy92Ca9DIs5jqot883G0Ln/PGXfm/07t39LS0=
k8s.io/apiextensions-apiserver v0.31.4/go.mod h1:hIW9YU8UsqZqIWGG99/gsdIU0Ar45Qd3A12QOe/rvpg=
k8s.io/apimachinery v0.31.4 h1:8xjE2C4CzhYVm9DGf60yohpNUh5AEBnPxCryPBECmlM=
k8s.io/apimachinery v0.31.4/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
k8s.io/client-go v0.31.4 h1:t4QEXt4jgHIkKKlx06+W3+1JOwAFU/2OPiOo7H92eRQ=
k8s.io/client-go v0.31.4/go.mod h1:kvuMro4sFYIa8sulL5Gi5GFqUPvfH2O/dXuKstbaaeg=
k8s.io/code-generator v0.31.4/go.mod h1:yMDt13Kn7m4MMZ4LxB1KBzdZjEyxzdT4b4qXq+lnI90=
k8s.io/api v0.36.0 h1:SgqDhZzHdOtMk40xVSvCXkP9ME0H05hPM3p9AB1kL80=
k8s.io/api v0.36.0/go.mod h1:m1LVrGPNYax5NBHdO+QuAedXyuzTt4RryI/qnmNvs34=
k8s.io/apiextensions-apiserver v0.36.0 h1:Wt7E8J+VBCbj4FjiBfDTK/neXDDjyJVJc7xfuOHImZ0=
k8s.io/apiextensions-apiserver v0.36.0/go.mod h1:kGDjH0msuiIB3tgsYRV0kS9GqpMYMUsQ3GHv7TApyug=
k8s.io/apimachinery v0.36.0 h1:jZyPzhd5Z+3h9vJLt0z9XdzW9VzNzWAUw+P1xZ9PXtQ=
k8s.io/apimachinery v0.36.0/go.mod h1:FklypaRJt6n5wUIwWXIP6GJlIpUizTgfo1T/As+Tyxc=
k8s.io/client-go v0.36.0 h1:pOYi7C4RHChYjMiHpZSpSbIM6ZxVbRXBy7CuiIwqA3c=
k8s.io/client-go v0.36.0/go.mod h1:ZKKcpwF0aLYfkHFCjillCKaTK/yBkEDHTDXCFY6AS9Y=
k8s.io/code-generator v0.36.0/go.mod h1:Tr2UhfBRdlyRoadfob9aPCmmGe8PUs5XPK9MEJ2nx+w=
k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
k8s.io/gengo v0.0.0-20211129171323-c02415ce4185/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70/go.mod h1:VH3AT8AaQOqiGjMF9p0/IM1Dj+82ZwjfxUP1IxaHE+8=
k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU=
k8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b/go.mod h1:CgujABENc3KuTrcsdpGmrrASjtQsWCT7R99mEV4U/fM=
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/klog/v2 v2.40.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc=
k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0=
k8s.io/kube-openapi v0.0.0-20220124234850-424119656bbf/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
k8s.io/mount-utils v0.31.4 h1:9aWJ5BpJvs6fdIo36wWIuCC6ZMNllUT0JSFsVNJloFI=
k8s.io/mount-utils v0.31.4/go.mod h1:HV/VYBUGqYUj4vt82YltzpWvgv8FPg0G9ItyInT3NPU=
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a h1:xCeOEAOoGYl2jnJoHkC3hkbPJgdATINPMAxaynU2Ovg=
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a/go.mod h1:uGBT7iTA6c6MvqUvSXIaYZo9ukscABYi2btjhvgKGZ0=
k8s.io/mount-utils v0.36.0 h1:ufsqGyCoPDh7p+6OIa1wv6oH9GqkQQ8XIfEOVfCV3g0=
k8s.io/mount-utils v0.36.0/go.mod h1:+I47UOG6FiUGVSy7VanjU/mQXLShMo3M7xBpGLzCub8=
k8s.io/streaming v0.36.0/go.mod h1:z6fV3D+NVkoeqRMtWwlUZK6U17SY/LqNzOxWL6GyR/s=
k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU=
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
kubevirt.io/api v1.2.2 h1:PeA937vsZawmKAsiiDQZJ/BbGH4OhEWsIzWrCNfmYXk=
kubevirt.io/api v1.2.2/go.mod h1:SbeR9ma4EwnaOZEUkh/lNz0kzYm5LPpEDE30vKXC5Zg=
kubevirt.io/containerized-data-importer-api v1.59.0 h1:GdDt9BlR0qHejpMaPfASbsG8JWDmBf1s7xZBj5W9qn0=
kubevirt.io/containerized-data-importer-api v1.59.0/go.mod h1:4yOGtCE7HvgKp7wftZZ3TBvDJ0x9d6N6KaRjRYcUFpE=
kubevirt.io/controller-lifecycle-operator-sdk/api v0.0.0-20220329064328-f3cc58c6ed90 h1:QMrd0nKP0BGbnxTqakhDZAUhGKxPiPiN5gSDqKUmGGc=
kubevirt.io/controller-lifecycle-operator-sdk/api v0.0.0-20220329064328-f3cc58c6ed90/go.mod h1:018lASpFYBsYN6XwmA2TIrPCx6e0gviTd/ZNtSitKgc=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
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 v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
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/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/structured-merge-diff/v6 v6.2.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/structured-merge-diff/v6 v6.3.2 h1:kwVWMx5yS1CrnFWA/2QHyRVJ8jM6dBA80uLmm0wJkk8=
sigs.k8s.io/structured-merge-diff/v6 v6.3.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View file

@ -134,14 +134,19 @@ func handle() {
}
// Create upstream driver (provides Identity, Controller, Node services)
upstreamDriver := service.NewKubevirtCSIDriver(virtClient,
identityClientset,
*infraClusterNamespace,
infraClusterLabelsMap,
storageClassEnforcement,
nodeID,
*runNodeService,
*runControllerService)
upstreamDriver := service.NewKubevirtCSIDriver().
WithIdentityService(identityClientset)
if *runControllerService {
upstreamDriver = upstreamDriver.WithControllerService(
virtClient,
*infraClusterNamespace,
infraClusterLabelsMap,
storageClassEnforcement,
)
}
if *runNodeService {
upstreamDriver = upstreamDriver.WithNodeService(nodeID)
}
// Wrap controller and node services with NFS/RWX support
var cs csi.ControllerServer

View file

@ -49,52 +49,3 @@ Selector labels
app.kubernetes.io/name: {{ include "kubernetes.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
wait-for-kubeconfig init container shared by the control-plane-side
Deployments (cluster-autoscaler, kccm, kcsi-controller) that mount the
*-admin-kubeconfig Secret provisioned asynchronously by Kamaji. The
Secret volume is declared optional so kubelet does not FailedMount while
Kamaji is still bootstrapping; this container polls the mounted path and
exits only when super-admin.svc appears, which happens after kubelet's
optional-Secret refresh cycle.
The 10m deadline stays strictly below the 15m HelmRelease
Install.Timeout set by cozystack-api for the Kubernetes kind (via the
release.cozystack.io/helm-install-timeout annotation) so the
CrashLoopBackOff surfaces before flux remediation fires and uninstalls
the Cluster CR.
The default image lives in images/busybox.tag and points directly at
docker.io by digest (not mirrored to ghcr.io like the other .tag files
here): the payload is a one-shot sh loop and the digest pin makes the
pull immutable. Operators in air-gapped or rate-limited environments
can override it via .Values.images.waitForKubeconfig (any registry
reference kubelet can pull). When the value is empty the chart falls
back to the bundled digest pin, preserving the prior default.
Call site owns the surrounding volumes block; the kubeconfig volume
must exist on the pod and mount at /etc/kubernetes/kubeconfig.
*/}}
{{- define "kubernetes.waitForAdminKubeconfig" -}}
- name: wait-for-kubeconfig
image: "{{ default (.Files.Get "images/busybox.tag" | trim) .Values.images.waitForKubeconfig }}"
command:
- sh
- -c
- |
set -eu
deadline=$(( $(date +%s) + 600 ))
until [ -s /etc/kubernetes/kubeconfig/super-admin.svc ]; do
if [ "$(date +%s)" -ge "$deadline" ]; then
echo "admin kubeconfig was not provisioned within 10m; exiting so the pod goes CrashLoopBackOff and surfaces in dashboards" >&2
exit 1
fi
echo "waiting for admin kubeconfig (provisioned by Kamaji, visible after kubelet Secret refresh)..."
sleep 5
done
volumeMounts:
- name: kubeconfig
mountPath: /etc/kubernetes/kubeconfig
readOnly: true
{{- end }}

View file

@ -1,14 +1,3 @@
{{- /*
Gate the control-plane-side workloads on the parent tenant having an etcd
DataStore. Without it no KamajiControlPlane is ever created, Kamaji never
provisions -admin-kubeconfig, and rendering these Deployments would cause
the wait-for-kubeconfig init to CrashLoopBackOff indefinitely, consuming
the parent HelmRelease install timeout and triggering the very uninstall
remediation cycle this chart is supposed to avoid. Rendering them only
when $etcd is set keeps the HelmRelease Ready while flux retries on its
interval and picks up the DataStore as soon as the Tenant chart finishes.
*/}}
{{- if .Values._namespace.etcd }}
---
apiVersion: apps/v1
kind: Deployment
@ -34,8 +23,6 @@ spec:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: "NoSchedule"
initContainers:
{{- include "kubernetes.waitForAdminKubeconfig" $ | nindent 6 }}
containers:
- image: "{{ $.Files.Get "images/cluster-autoscaler.tag" | trim }}"
name: cluster-autoscaler
@ -69,7 +56,6 @@ spec:
name: cloud-config
- secret:
secretName: {{ .Release.Name }}-admin-kubeconfig
optional: true
name: kubeconfig
serviceAccountName: {{ .Release.Name }}-cluster-autoscaler
terminationGracePeriodSeconds: 10
@ -119,4 +105,3 @@ rules:
- list
- update
- watch
{{- end }}

View file

@ -1,15 +1,4 @@
{{- $etcd := .Values._namespace.etcd }}
{{- /*
When $etcd is empty, the parent Tenant application has not populated
_namespace.etcd in cozystack-values yet - either the operator forgot to
set etcd: true on an ancestor Tenant, or the Tenant HelmRelease is still
reconciling. Either way, rendering a KamajiControlPlane with an empty
dataStoreName would be rejected by Kamaji's admission webhook and the
HelmRelease would fail to install, triggering remediation. Instead, emit
a single ConfigMap as a user-visible status beacon and skip the rest so
flux marks the HelmRelease Ready and retries its 5m reconcile loop until
the Tenant chart catches up.
*/}}
{{- $ingress := .Values._namespace.ingress }}
{{- $host := .Values._namespace.host }}
{{- $kubevirtmachinetemplateNames := list }}
@ -95,26 +84,6 @@ spec:
- name: default
pod: {}
{{- end }}
{{- if not $etcd }}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-awaiting-etcd
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/name: kubernetes
app.kubernetes.io/instance: {{ .Release.Name }}
data:
status: "awaiting-etcd"
message: |
No DataStore is available for this tenant Kubernetes cluster. The parent
Tenant application has not populated _namespace.etcd. Set spec.etcd: true
on an ancestor Tenant (usually tenant-root) and wait for its HelmRelease
to reconcile - this HelmRelease will pick up the DataStore on its next
5m reconcile loop and provision the cluster.
{{- else }}
---
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
@ -435,4 +404,3 @@ metadata:
spec:
{{- .spec | toYaml | nindent 2 }}
{{- end }}
{{- end }}

View file

@ -1,4 +1,3 @@
{{- if .Values._namespace.etcd }}
kind: Deployment
apiVersion: apps/v1
metadata:
@ -25,8 +24,6 @@ spec:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: "NoSchedule"
initContainers:
{{- include "kubernetes.waitForAdminKubeconfig" $ | nindent 6 }}
containers:
- name: csi-driver
imagePullPolicy: Always
@ -237,6 +234,4 @@ spec:
emptyDir: {}
- secret:
secretName: {{ .Release.Name }}-admin-kubeconfig
optional: true
name: kubeconfig
{{- end }}

View file

@ -1,4 +1,4 @@
{{- if and .Values.addons.certManager.enabled .Values._namespace.etcd }}
{{- if .Values.addons.certManager.enabled }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:

View file

@ -8,7 +8,7 @@ cert-manager:
{{- end }}
{{- end }}
{{- if and .Values.addons.certManager.enabled .Values._namespace.etcd }}
{{- if .Values.addons.certManager.enabled }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:

View file

@ -14,7 +14,6 @@ cilium:
{{- end }}
{{- end }}
{{- if .Values._namespace.etcd }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
@ -56,4 +55,3 @@ spec:
- name: {{ .Release.Name }}-gateway-api-crds
namespace: {{ .Release.Namespace }}
{{- end }}
{{- end }}

View file

@ -4,7 +4,6 @@ coredns:
clusterIP: "10.95.0.10"
{{- end }}
{{- if .Values._namespace.etcd }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
@ -43,4 +42,3 @@ spec:
{{- end }}
- name: {{ .Release.Name }}-cilium
namespace: {{ .Release.Namespace }}
{{- end }}

View file

@ -1,4 +1,3 @@
{{- if .Values._namespace.etcd }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
@ -40,4 +39,3 @@ spec:
- name: {{ .Release.Name }}
namespace: {{ .Release.Namespace }}
{{- end }}
{{- end }}

View file

@ -1,4 +1,4 @@
{{- if and .Values.addons.fluxcd.enabled .Values._namespace.etcd }}
{{- if .Values.addons.fluxcd.enabled }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:

View file

@ -1,4 +1,4 @@
{{- if and $.Values.addons.gatewayAPI.enabled $.Values._namespace.etcd }}
{{- if $.Values.addons.gatewayAPI.enabled }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:

View file

@ -1,12 +1,4 @@
{{- define "cozystack.defaultGpuOperatorValues" -}}
{{- if .Values.addons.hami.enabled }}
gpu-operator:
devicePlugin:
enabled: false
{{- end }}
{{- end }}
{{- if and .Values.addons.gpuOperator.enabled .Values._namespace.etcd }}
{{- if .Values.addons.gpuOperator.enabled }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
@ -37,12 +29,9 @@ spec:
force: true
remediation:
retries: -1
{{- $defaults := fromYaml (include "cozystack.defaultGpuOperatorValues" .) }}
{{- $overrides := deepCopy (default (dict) .Values.addons.gpuOperator.valuesOverride) }}
{{- $merged := mergeOverwrite (default (dict) $defaults) $overrides }}
{{- if $merged }}
{{- with .Values.addons.gpuOperator.valuesOverride }}
values:
{{- toYaml $merged | nindent 4 }}
{{- toYaml . | nindent 4 }}
{{- end }}
dependsOn:

View file

@ -1,49 +0,0 @@
{{- if and .Values.addons.hami.enabled .Values._namespace.etcd }}
{{- if not .Values.addons.gpuOperator.enabled }}
{{- fail "addons.hami requires addons.gpuOperator to be enabled" }}
{{- end }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: {{ .Release.Name }}-hami
labels:
cozystack.io/repository: system
cozystack.io/target-cluster-name: {{ .Release.Name }}
sharding.fluxcd.io/key: tenants
spec:
releaseName: hami
chartRef:
kind: ExternalArtifact
name: cozystack-kubernetes-application-kubevirt-kubernetes-hami
namespace: cozy-system
kubeConfig:
secretRef:
name: {{ .Release.Name }}-admin-kubeconfig
key: super-admin.svc
targetNamespace: cozy-hami
storageNamespace: cozy-hami
interval: 5m
timeout: 10m
install:
createNamespace: true
remediation:
retries: -1
upgrade:
force: true
remediation:
retries: -1
{{- with .Values.addons.hami.valuesOverride }}
values:
{{- toYaml . | nindent 4 }}
{{- end }}
dependsOn:
{{- if lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" .Release.Namespace .Release.Name }}
- name: {{ .Release.Name }}
namespace: {{ .Release.Namespace }}
{{- end }}
- name: {{ .Release.Name }}-cilium
namespace: {{ .Release.Namespace }}
- name: {{ .Release.Name }}-gpu-operator
namespace: {{ .Release.Namespace }}
{{- end }}

View file

@ -20,7 +20,7 @@ ingress-nginx:
node-role.kubernetes.io/ingress-nginx: ""
{{- end }}
{{- if and .Values.addons.ingressNginx.enabled .Values._namespace.etcd }}
{{- if .Values.addons.ingressNginx.enabled }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:

View file

@ -1,4 +1,3 @@
{{- if .Values._namespace.etcd }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
@ -37,4 +36,3 @@ spec:
namespace: {{ .Release.Namespace }}
- name: {{ .Release.Name }}-prometheus-operator-crds
namespace: {{ .Release.Namespace }}
{{- end }}

View file

@ -1,6 +1,6 @@
{{- $targetTenant := .Values._namespace.monitoring }}
{{- $clusterDomain := (index .Values._cluster "cluster-domain") | default "cozy.local" }}
{{- if and .Values.addons.monitoringAgents.enabled .Values._namespace.etcd }}
{{- if .Values.addons.monitoringAgents.enabled }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:

View file

@ -1,4 +1,3 @@
{{- if .Values._namespace.etcd }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
@ -32,4 +31,3 @@ spec:
- name: {{ .Release.Name }}
namespace: {{ .Release.Namespace }}
{{- end }}
{{- end }}

View file

@ -1,4 +1,4 @@
{{- if and .Values.addons.velero.enabled .Values._namespace.etcd }}
{{- if .Values.addons.velero.enabled }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:

View file

@ -1,4 +1,4 @@
{{- if and .Values.addons.monitoringAgents.enabled .Values._namespace.etcd }}
{{- if .Values.addons.monitoringAgents.enabled }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:

View file

@ -24,7 +24,7 @@ vertical-pod-autoscaler:
memory: 1600Mi
{{- end }}
{{- if and .Values.addons.monitoringAgents.enabled .Values._namespace.etcd }}
{{- if .Values.addons.monitoringAgents.enabled }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:

View file

@ -1,4 +1,4 @@
{{- if and .Values.addons.monitoringAgents.enabled .Values._namespace.etcd }}
{{- if .Values.addons.monitoringAgents.enabled }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:

View file

@ -1,4 +1,3 @@
{{- if .Values._namespace.etcd }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
@ -34,4 +33,3 @@ spec:
- name: {{ .Release.Name }}
namespace: {{ .Release.Namespace }}
{{- end }}
{{- end }}

View file

@ -1,4 +1,3 @@
{{- if .Values._namespace.etcd }}
apiVersion: apps/v1
kind: Deployment
metadata:
@ -23,8 +22,6 @@ spec:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: "NoSchedule"
initContainers:
{{- include "kubernetes.waitForAdminKubeconfig" $ | nindent 6 }}
containers:
- name: kubevirt-cloud-controller-manager
args:
@ -58,7 +55,5 @@ spec:
name: cloud-config
- secret:
secretName: {{ .Release.Name }}-admin-kubeconfig
optional: true
name: kubeconfig
serviceAccountName: {{ .Release.Name }}-kccm
{{- end }}

View file

@ -1,155 +0,0 @@
suite: admin-kubeconfig wait guards
release:
name: test
namespace: tenant-root
values:
- values-ci.yaml
tests:
- it: cluster-autoscaler mounts admin-kubeconfig as optional
template: templates/cluster-autoscaler/deployment.yaml
documentSelector:
path: kind
value: Deployment
asserts:
- equal:
path: spec.template.spec.volumes[?(@.name=="kubeconfig")].secret.secretName
value: test-admin-kubeconfig
- equal:
path: spec.template.spec.volumes[?(@.name=="kubeconfig")].secret.optional
value: true
- it: cluster-autoscaler waits for admin-kubeconfig via initContainer
template: templates/cluster-autoscaler/deployment.yaml
documentSelector:
path: kind
value: Deployment
asserts:
- equal:
path: spec.template.spec.initContainers[0].name
value: wait-for-kubeconfig
- contains:
path: spec.template.spec.initContainers[0].volumeMounts
content:
name: kubeconfig
mountPath: /etc/kubernetes/kubeconfig
readOnly: true
- it: kccm mounts admin-kubeconfig as optional
template: templates/kccm/manager.yaml
documentSelector:
path: kind
value: Deployment
asserts:
- equal:
path: spec.template.spec.volumes[?(@.name=="kubeconfig")].secret.secretName
value: test-admin-kubeconfig
- equal:
path: spec.template.spec.volumes[?(@.name=="kubeconfig")].secret.optional
value: true
- it: kccm waits for admin-kubeconfig via initContainer
template: templates/kccm/manager.yaml
documentSelector:
path: kind
value: Deployment
asserts:
- equal:
path: spec.template.spec.initContainers[0].name
value: wait-for-kubeconfig
- contains:
path: spec.template.spec.initContainers[0].volumeMounts
content:
name: kubeconfig
mountPath: /etc/kubernetes/kubeconfig
readOnly: true
- it: csi controller mounts admin-kubeconfig as optional
template: templates/csi/deploy.yaml
documentSelector:
path: kind
value: Deployment
asserts:
- equal:
path: spec.template.spec.volumes[?(@.name=="kubeconfig")].secret.secretName
value: test-admin-kubeconfig
- equal:
path: spec.template.spec.volumes[?(@.name=="kubeconfig")].secret.optional
value: true
- it: csi controller waits for admin-kubeconfig via initContainer
template: templates/csi/deploy.yaml
documentSelector:
path: kind
value: Deployment
asserts:
- equal:
path: spec.template.spec.initContainers[0].name
value: wait-for-kubeconfig
- contains:
path: spec.template.spec.initContainers[0].volumeMounts
content:
name: kubeconfig
mountPath: /etc/kubernetes/kubeconfig
readOnly: true
- it: wait-for-kubeconfig defaults to bundled busybox digest pin
template: templates/cluster-autoscaler/deployment.yaml
documentSelector:
path: kind
value: Deployment
asserts:
- matchRegex:
path: spec.template.spec.initContainers[0].image
pattern: '^docker\.io/library/busybox:[^@]+@sha256:[0-9a-f]{64}$'
- it: wait-for-kubeconfig honours images.waitForKubeconfig override
template: templates/cluster-autoscaler/deployment.yaml
documentSelector:
path: kind
value: Deployment
set:
images:
waitForKubeconfig: "registry.example.test/library/busybox:1.37.0@sha256:0000000000000000000000000000000000000000000000000000000000000000"
asserts:
- equal:
path: spec.template.spec.initContainers[0].image
value: "registry.example.test/library/busybox:1.37.0@sha256:0000000000000000000000000000000000000000000000000000000000000000"
- it: cluster.yaml renders and wires dataStoreName when tenant has etcd
template: templates/cluster.yaml
documentSelector:
path: kind
value: KamajiControlPlane
asserts:
- equal:
path: spec.dataStoreName
value: tenant-root
- it: cluster.yaml skips Cluster resources when tenant has no etcd DataStore
# Must NOT fail rendering - the parent Tenant chart populates
# _namespace.etcd asynchronously, so rendering failures here would cause
# flux install remediation on every cold bootstrap. Instead, emit only a
# ConfigMap status beacon so the HelmRelease reports Ready while flux
# retries on its interval until the DataStore appears.
template: templates/cluster.yaml
set:
_namespace:
etcd: ""
monitoring: ""
ingress: ""
seaweedfs: ""
host: ""
asserts:
- hasDocuments:
count: 1
- isKind:
of: ConfigMap
- equal:
path: metadata.name
value: test-awaiting-etcd
- equal:
path: data.status
value: awaiting-etcd

View file

@ -1,99 +0,0 @@
suite: GPU Operator HelmRelease HAMi integration tests
templates:
- templates/helmreleases/gpu-operator.yaml
values:
- values-ci.yaml
tests:
- it: should disable devicePlugin when hami is enabled
set:
addons:
gpuOperator:
enabled: true
valuesOverride: {}
hami:
enabled: true
valuesOverride: {}
asserts:
- equal:
path: spec.values.gpu-operator.devicePlugin.enabled
value: false
- it: should not have values when hami is disabled and no overrides
set:
addons:
gpuOperator:
enabled: true
valuesOverride: {}
hami:
enabled: false
valuesOverride: {}
asserts:
- notExists:
path: spec.values
- it: should apply hami defaults when valuesOverride key is omitted
set:
addons:
gpuOperator:
enabled: true
hami:
enabled: true
asserts:
- equal:
path: spec.values.gpu-operator.devicePlugin.enabled
value: false
- it: should allow user overrides to merge with hami defaults
set:
addons:
gpuOperator:
enabled: true
valuesOverride:
gpu-operator:
driver:
enabled: false
hami:
enabled: true
valuesOverride: {}
asserts:
- equal:
path: spec.values.gpu-operator.devicePlugin.enabled
value: false
- equal:
path: spec.values.gpu-operator.driver.enabled
value: false
- it: should let user explicitly override devicePlugin.enabled to true with hami enabled
set:
addons:
gpuOperator:
enabled: true
valuesOverride:
gpu-operator:
devicePlugin:
enabled: true
driver:
enabled: false
hami:
enabled: true
valuesOverride: {}
asserts:
- equal:
path: spec.values.gpu-operator.devicePlugin.enabled
value: true
- equal:
path: spec.values.gpu-operator.driver.enabled
value: false
- it: should not render when gpuOperator is disabled
set:
addons:
gpuOperator:
enabled: false
valuesOverride: {}
hami:
enabled: false
valuesOverride: {}
asserts:
- hasDocuments:
count: 0

View file

@ -1,153 +0,0 @@
suite: HAMi HelmRelease tests
templates:
- templates/helmreleases/hami.yaml
values:
- values-ci.yaml
tests:
- it: should not render when hami is disabled
set:
addons:
hami:
enabled: false
valuesOverride: {}
gpuOperator:
enabled: true
valuesOverride: {}
asserts:
- hasDocuments:
count: 0
- it: should render HelmRelease when hami is enabled
set:
addons:
hami:
enabled: true
valuesOverride: {}
gpuOperator:
enabled: true
valuesOverride: {}
asserts:
- hasDocuments:
count: 1
- isKind:
of: HelmRelease
- it: should fail when gpuOperator is not enabled
set:
addons:
hami:
enabled: true
valuesOverride: {}
gpuOperator:
enabled: false
valuesOverride: {}
asserts:
- failedTemplate:
errorMessage: "addons.hami requires addons.gpuOperator to be enabled"
- it: should have correct metadata labels
set:
addons:
hami:
enabled: true
valuesOverride: {}
gpuOperator:
enabled: true
valuesOverride: {}
asserts:
- equal:
path: metadata.labels["cozystack.io/repository"]
value: system
- equal:
path: metadata.labels["sharding.fluxcd.io/key"]
value: tenants
- it: should use ExternalArtifact chartRef
set:
addons:
hami:
enabled: true
valuesOverride: {}
gpuOperator:
enabled: true
valuesOverride: {}
asserts:
- equal:
path: spec.chartRef.kind
value: ExternalArtifact
- equal:
path: spec.chartRef.namespace
value: cozy-system
- it: should target cozy-hami namespace
set:
addons:
hami:
enabled: true
valuesOverride: {}
gpuOperator:
enabled: true
valuesOverride: {}
asserts:
- equal:
path: spec.targetNamespace
value: cozy-hami
- equal:
path: spec.storageNamespace
value: cozy-hami
- it: should depend on gpu-operator and cilium
release:
name: test
namespace: test-ns
set:
addons:
hami:
enabled: true
valuesOverride: {}
gpuOperator:
enabled: true
valuesOverride: {}
asserts:
- contains:
path: spec.dependsOn
content:
name: test-cilium
namespace: test-ns
- contains:
path: spec.dependsOn
content:
name: test-gpu-operator
namespace: test-ns
- it: should not render spec.values when valuesOverride is empty
set:
addons:
hami:
enabled: true
valuesOverride: {}
gpuOperator:
enabled: true
valuesOverride: {}
asserts:
- hasDocuments:
count: 1
- notExists:
path: spec.values
- it: should pass through valuesOverride
set:
addons:
hami:
enabled: true
valuesOverride:
hami:
devicePlugin:
deviceSplitCount: 5
gpuOperator:
enabled: true
valuesOverride: {}
asserts:
- equal:
path: spec.values.hami.devicePlugin.deviceSplitCount
value: 5

View file

@ -1,9 +0,0 @@
_namespace:
etcd: ""
monitoring: ""
ingress: ""
seaweedfs: ""
host: ""
_cluster:
cluster-domain: cozy.local
nodeGroups: null

View file

@ -1,9 +0,0 @@
_namespace:
etcd: tenant-root
monitoring: ""
ingress: ""
seaweedfs: ""
host: ""
_cluster:
cluster-domain: cozy.local
nodeGroups: null

View file

@ -149,7 +149,6 @@
"fluxcd",
"gatewayAPI",
"gpuOperator",
"hami",
"ingressNginx",
"monitoringAgents",
"velero",
@ -269,28 +268,6 @@
}
}
},
"hami": {
"description": "HAMi GPU virtualization middleware.",
"type": "object",
"default": {},
"required": [
"enabled",
"valuesOverride"
],
"properties": {
"enabled": {
"description": "Enable HAMi (requires GPU Operator).",
"type": "boolean",
"default": false
},
"valuesOverride": {
"description": "Custom Helm values overrides.",
"type": "object",
"default": {},
"x-kubernetes-preserve-unknown-fields": true
}
}
},
"ingressNginx": {
"description": "Ingress-NGINX controller.",
"type": "object",
@ -653,18 +630,6 @@
}
}
}
},
"images": {
"description": "Optional image overrides for air-gapped or rate-limited registries.",
"type": "object",
"default": {},
"properties": {
"waitForKubeconfig": {
"description": "Image used by the wait-for-kubeconfig init container. Empty falls back to images/busybox.tag.",
"type": "string",
"default": ""
}
}
}
}
}

View file

@ -94,10 +94,6 @@ host: ""
## @field {bool} enabled - Enable FluxCD.
## @field {object} valuesOverride - Custom Helm values overrides.
## @typedef {struct} HAMiAddon - HAMi GPU virtualization middleware.
## @field {bool} enabled - Enable HAMi (requires GPU Operator).
## @field {object} valuesOverride - Custom Helm values overrides.
## @typedef {struct} MonitoringAgentsAddon - Monitoring agents (Fluent Bit, VMAgents).
## @field {bool} enabled - Enable monitoring agents.
## @field {object} valuesOverride - Custom Helm values overrides.
@ -118,7 +114,6 @@ host: ""
## @field {GatewayAPIAddon} gatewayAPI - Gateway API addon.
## @field {IngressNginxAddon} ingressNginx - Ingress-NGINX controller.
## @field {GPUOperatorAddon} gpuOperator - NVIDIA GPU Operator.
## @field {HAMiAddon} hami - HAMi GPU virtualization middleware.
## @field {FluxCDAddon} fluxcd - FluxCD GitOps operator.
## @field {MonitoringAgentsAddon} monitoringAgents - Monitoring agents.
## @field {VerticalPodAutoscalerAddon} verticalPodAutoscaler - Vertical Pod Autoscaler.
@ -142,9 +137,6 @@ addons:
gpuOperator:
enabled: false
valuesOverride: {}
hami:
enabled: false
valuesOverride: {}
fluxcd:
enabled: false
valuesOverride: {}
@ -205,10 +197,3 @@ controlPlane:
server:
resources: {}
resourcesPreset: "micro"
## @typedef {struct} Images - Optional image overrides for chart-internal helpers.
## @field {string} [waitForKubeconfig] - Image used by the wait-for-kubeconfig init container. Empty falls back to images/busybox.tag.
## @param {Images} images - Optional image overrides for air-gapped or rate-limited registries.
images:
waitForKubeconfig: ""

View file

@ -133,13 +133,12 @@ See:
### Bootstrap (recovery) parameters
| Name | Description | Type | Value |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------- |
| `bootstrap` | Bootstrap configuration. | `object` | `{}` |
| `bootstrap.enabled` | Whether to restore from a backup. | `bool` | `false` |
| `bootstrap.recoveryTime` | Timestamp (RFC3339) for point-in-time recovery; empty means latest. | `string` | `""` |
| `bootstrap.oldName` | Previous cluster name before deletion. | `string` | `""` |
| `bootstrap.serverName` | Barman server name (S3 path prefix) used by the original cluster when writing backups. Set this only when the original cluster had an explicit barmanObjectStore.serverName that differed from its Kubernetes resource name. | `string` | `""` |
| Name | Description | Type | Value |
| ------------------------ | ------------------------------------------------------------------- | -------- | ------- |
| `bootstrap` | Bootstrap configuration. | `object` | `{}` |
| `bootstrap.enabled` | Whether to restore from a backup. | `bool` | `false` |
| `bootstrap.recoveryTime` | Timestamp (RFC3339) for point-in-time recovery; empty means latest. | `string` | `""` |
| `bootstrap.oldName` | Previous cluster name before deletion. | `string` | `""` |
## Parameter examples and reference

View file

@ -32,9 +32,6 @@ spec:
- name: {{ .Values.bootstrap.oldName }}
barmanObjectStore:
destinationPath: {{ .Values.backup.destinationPath }}
{{- if .Values.bootstrap.serverName }}
serverName: {{ .Values.bootstrap.serverName }}
{{- end }}
endpointURL: {{ .Values.backup.endpointURL }}
s3Credentials:
accessKeyId:

View file

@ -254,11 +254,6 @@
"description": "Timestamp (RFC3339) for point-in-time recovery; empty means latest.",
"type": "string",
"default": ""
},
"serverName": {
"description": "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.",
"type": "string",
"default": ""
}
}
}

View file

@ -154,7 +154,6 @@ backup:
## @field {bool} enabled - Whether to restore from a backup.
## @field {string} [recoveryTime] - Timestamp (RFC3339) for point-in-time recovery; empty means latest.
## @field {string} oldName - Previous cluster name before deletion.
## @field {string} [serverName] - Barman server name (S3 path prefix) used by the original cluster when writing backups. Set this only when the original cluster had an explicit barmanObjectStore.serverName that differed from its Kubernetes resource name.
## @param {Bootstrap} bootstrap - Bootstrap configuration.
bootstrap:
@ -162,4 +161,3 @@ bootstrap:
# example: 2020-11-26 15:22:00.00000+00
recoveryTime: ""
oldName: ""
serverName: ""

View file

@ -3,6 +3,3 @@ include ../../../hack/package.mk
generate:
cozyvalues-gen -m 'tenant' -v values.yaml -s values.schema.json -r README.md -g ../../../api/apps/v1alpha1/tenant/types.go
../../../hack/update-crd.sh
test:
helm unittest .

View file

@ -1,28 +0,0 @@
{{- $exposeMode := (index .Values._cluster "expose-mode") | default "externalIPs" }}
{{- $exposeIngress := (index .Values._cluster "expose-ingress") | default "tenant-root" }}
{{- $exposeIPs := (index .Values._cluster "expose-external-ips") | default "" | nospace }}
{{- $ipsList := list }}
{{- range splitList "," $exposeIPs }}
{{- $ip := . | trim }}
{{- if $ip }}
{{- $ipsList = append $ipsList $ip }}
{{- end }}
{{- end }}
{{- $isPublishingIngressLB := and
(eq $exposeMode "loadBalancer")
(eq $exposeIngress .Release.Namespace)
.Values.ingress }}
{{- if and $isPublishingIngressLB $ipsList }}
apiVersion: cilium.io/v2
kind: CiliumLoadBalancerIPPool
metadata:
name: {{ trimPrefix "tenant-" .Release.Namespace }}-exposure
spec:
blocks:
{{- range $ipsList }}
- cidr: {{ . }}{{ if not (contains "/" .) }}/{{ if contains ":" . }}128{{ else }}32{{ end }}{{ end }}
{{- end }}
serviceSelector:
matchLabels:
"io.kubernetes.service.namespace": {{ .Release.Namespace | quote }}
{{- end }}

View file

@ -1,141 +0,0 @@
suite: tenant CiliumLoadBalancerIPPool rendering for publishing.exposure=loadBalancer
templates:
- templates/cilium-lb-pool.yaml
release:
name: tenant-root
namespace: tenant-root
tests:
- it: default exposure (externalIPs) renders no pool
set:
ingress: true
_cluster:
expose-ingress: tenant-root
expose-external-ips: "192.0.2.10,192.0.2.11"
asserts:
- hasDocuments:
count: 0
- it: loadBalancer mode in publishing tenant with ingress=true renders v2 pool with namespace-only selector
set:
ingress: true
_cluster:
expose-ingress: tenant-root
expose-external-ips: "192.0.2.10,192.0.2.11"
expose-mode: loadBalancer
asserts:
- hasDocuments:
count: 1
- equal:
path: apiVersion
value: cilium.io/v2
- equal:
path: kind
value: CiliumLoadBalancerIPPool
- equal:
path: metadata.name
value: root-exposure
- equal:
path: spec.blocks
value:
- cidr: 192.0.2.10/32
- cidr: 192.0.2.11/32
- equal:
path: spec.serviceSelector.matchLabels["io.kubernetes.service.namespace"]
value: tenant-root
- notExists:
path: spec.serviceSelector.matchLabels["app.kubernetes.io/name"]
- it: loadBalancer mode with IPv6 emits /128 CIDR
set:
ingress: true
_cluster:
expose-ingress: tenant-root
expose-external-ips: "2001:db8::1"
expose-mode: loadBalancer
asserts:
- equal:
path: spec.blocks
value:
- cidr: 2001:db8::1/128
- it: loadBalancer mode with mixed IPv4 and IPv6 emits correct CIDR per family
set:
ingress: true
_cluster:
expose-ingress: tenant-root
expose-external-ips: "192.0.2.10,2001:db8::1"
expose-mode: loadBalancer
asserts:
- equal:
path: spec.blocks
value:
- cidr: 192.0.2.10/32
- cidr: 2001:db8::1/128
- it: loadBalancer mode accepts pre-CIDR input without double-suffixing
set:
ingress: true
_cluster:
expose-ingress: tenant-root
expose-external-ips: "192.0.2.10/32,2001:db8::1/128"
expose-mode: loadBalancer
asserts:
- equal:
path: spec.blocks
value:
- cidr: 192.0.2.10/32
- cidr: 2001:db8::1/128
- it: loadBalancer mode filters out empty entries from externalIPs (trailing, leading, repeated commas)
set:
ingress: true
_cluster:
expose-ingress: tenant-root
expose-external-ips: "192.0.2.10,,192.0.2.11,"
expose-mode: loadBalancer
asserts:
- hasDocuments:
count: 1
- equal:
path: spec.blocks
value:
- cidr: 192.0.2.10/32
- cidr: 192.0.2.11/32
- it: loadBalancer mode with ingress=false in publishing tenant renders no pool
set:
ingress: false
_cluster:
expose-ingress: tenant-root
expose-external-ips: "192.0.2.10"
expose-mode: loadBalancer
asserts:
- hasDocuments:
count: 0
- it: loadBalancer mode in a non-publishing tenant renders no pool
set:
ingress: true
_cluster:
expose-ingress: tenant-root
expose-external-ips: "192.0.2.10"
expose-mode: loadBalancer
release:
name: tenant-u1
namespace: tenant-u1
asserts:
- hasDocuments:
count: 0
- it: loadBalancer mode in publishing tenant with empty externalIPs renders no pool
set:
ingress: true
_cluster:
expose-ingress: tenant-root
expose-external-ips: ""
expose-mode: loadBalancer
asserts:
- hasDocuments:
count: 0

View file

@ -36,32 +36,31 @@ virtctl ssh <user>@<vm>
### Common parameters
| Name | Description | Type | Value |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ----------- |
| `external` | Enable external access from outside the cluster. | `bool` | `false` |
| `externalMethod` | Method to pass through traffic to the VM. | `string` | `PortList` |
| `externalPorts` | Ports to forward from outside the cluster. | `[]int` | `[22]` |
| `externalAllowICMP` | Whether to accept ICMP traffic to the VM in PortList mode (preserves ping and PMTU discovery). No effect in WholeIP mode. Default true so ping behaves as users expect even when port filtering is in effect. | `bool` | `true` |
| `runStrategy` | Requested running state of the VirtualMachineInstance | `string` | `Always` |
| `instanceType` | Virtual Machine instance type. | `string` | `u1.medium` |
| `instanceProfile` | Virtual Machine preferences profile. | `string` | `ubuntu` |
| `disks` | List of disks to attach. | `[]object` | `[]` |
| `disks[i].name` | Disk name. | `string` | `""` |
| `disks[i].bus` | Disk bus type (e.g. "sata"). | `string` | `""` |
| `networks` | Networks to attach the VM to. | `[]object` | `[]` |
| `networks[i].name` | Network attachment name. | `string` | `""` |
| `subnets` | Deprecated: use networks instead. | `[]object` | `[]` |
| `subnets[i].name` | Network attachment name. | `string` | `""` |
| `gpus` | List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM). | `[]object` | `[]` |
| `gpus[i].name` | The name of the GPU resource to attach. | `string` | `""` |
| `cpuModel` | Model specifies the CPU model inside the VMI. List of available models https://github.com/libvirt/libvirt/tree/master/src/cpu_map | `string` | `""` |
| `resources` | Resource configuration for the virtual machine. | `object` | `{}` |
| `resources.cpu` | Number of CPU cores allocated. | `quantity` | `""` |
| `resources.memory` | Amount of memory allocated. | `quantity` | `""` |
| `resources.sockets` | Number of CPU sockets (vCPU topology). | `quantity` | `""` |
| `sshKeys` | List of SSH public keys for authentication. | `[]string` | `[]` |
| `cloudInit` | Cloud-init user data. | `string` | `""` |
| `cloudInitSeed` | Seed string to generate SMBIOS UUID for the VM. | `string` | `""` |
| Name | Description | Type | Value |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ---------- | ----------- |
| `external` | Enable external access from outside the cluster. | `bool` | `false` |
| `externalMethod` | Method to pass through traffic to the VM. | `string` | `PortList` |
| `externalPorts` | Ports to forward from outside the cluster. | `[]int` | `[22]` |
| `runStrategy` | Requested running state of the VirtualMachineInstance | `string` | `Always` |
| `instanceType` | Virtual Machine instance type. | `string` | `u1.medium` |
| `instanceProfile` | Virtual Machine preferences profile. | `string` | `ubuntu` |
| `disks` | List of disks to attach. | `[]object` | `[]` |
| `disks[i].name` | Disk name. | `string` | `""` |
| `disks[i].bus` | Disk bus type (e.g. "sata"). | `string` | `""` |
| `networks` | Networks to attach the VM to. | `[]object` | `[]` |
| `networks[i].name` | Network attachment name. | `string` | `""` |
| `subnets` | Deprecated: use networks instead. | `[]object` | `[]` |
| `subnets[i].name` | Network attachment name. | `string` | `""` |
| `gpus` | List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM). | `[]object` | `[]` |
| `gpus[i].name` | The name of the GPU resource to attach. | `string` | `""` |
| `cpuModel` | Model specifies the CPU model inside the VMI. List of available models https://github.com/libvirt/libvirt/tree/master/src/cpu_map | `string` | `""` |
| `resources` | Resource configuration for the virtual machine. | `object` | `{}` |
| `resources.cpu` | Number of CPU cores allocated. | `quantity` | `""` |
| `resources.memory` | Amount of memory allocated. | `quantity` | `""` |
| `resources.sockets` | Number of CPU sockets (vCPU topology). | `quantity` | `""` |
| `sshKeys` | List of SSH public keys for authentication. | `[]string` | `[]` |
| `cloudInit` | Cloud-init user data. | `string` | `""` |
| `cloudInitSeed` | Seed string to generate SMBIOS UUID for the VM. | `string` | `""` |
## U Series

View file

@ -9,10 +9,7 @@ metadata:
{{- if .Values.external }}
service.kubernetes.io/service-proxy-name: "cozy-proxy"
annotations:
networking.cozystack.io/wholeIP: {{ ternary "true" "false" (eq .Values.externalMethod "WholeIP") | quote }}
{{- if eq .Values.externalMethod "PortList" }}
networking.cozystack.io/allowICMP: {{ ternary "true" "false" (ne .Values.externalAllowICMP false) | quote }}
{{- end }}
networking.cozystack.io/wholeIP: "true"
{{- end }}
spec:
type: {{ ternary "LoadBalancer" "ClusterIP" .Values.external }}

View file

@ -26,11 +26,6 @@
"type": "integer"
}
},
"externalAllowICMP": {
"description": "Whether to accept ICMP traffic to the VM in PortList mode (preserves ping and PMTU discovery). No effect in WholeIP mode. Default true so ping behaves as users expect even when port filtering is in effect.",
"type": "boolean",
"default": true
},
"runStrategy": {
"description": "Requested running state of the VirtualMachineInstance",
"type": "string",

View file

@ -31,9 +31,6 @@ externalMethod: PortList
externalPorts:
- 22
## @param {bool} externalAllowICMP - Whether to accept ICMP traffic to the VM in PortList mode (preserves ping and PMTU discovery). No effect in WholeIP mode. Default true so ping behaves as users expect even when port filtering is in effect.
externalAllowICMP: true
## @enum {string} RunStrategy - Requested running state of the VirtualMachineInstance
## @value Always - VMI should always be running
## @value Halted - VMI should never be running

View file

@ -1,4 +1,4 @@
FROM golang:1.25-alpine as builder
FROM golang:1.26-alpine AS builder
ARG TARGETOS
ARG TARGETARCH

View file

@ -1,24 +0,0 @@
---
apiVersion: cozystack.io/v1alpha1
kind: PackageSource
metadata:
name: cozystack.hami
spec:
sourceRef:
kind: OCIRepository
name: cozystack-packages
namespace: cozy-system
path: /
variants:
- name: default
dependsOn:
- cozystack.gpu-operator
components:
- name: hami
path: system/hami
valuesFiles:
- values.yaml
install:
privileged: true
namespace: cozy-hami
releaseName: hami

View file

@ -52,8 +52,6 @@ spec:
path: system/cilium
- name: kubernetes-gpu-operator
path: system/gpu-operator
- name: kubernetes-hami
path: system/hami
- name: kubernetes-vertical-pod-autoscaler
path: system/vertical-pod-autoscaler
- name: kubernetes-prometheus-operator-crds

View file

@ -30,7 +30,6 @@ stringData:
expose-services: {{ .Values.publishing.exposedServices | join "," | quote }}
expose-ingress: {{ .Values.publishing.ingressName | quote }}
expose-external-ips: {{ .Values.publishing.externalIPs | join "," | quote }}
expose-mode: {{ .Values.publishing.exposure | default "externalIPs" | quote }}
cluster-domain: {{ .Values.networking.clusterDomain | quote }}
api-server-endpoint: {{ .Values.publishing.apiServerEndpoint | quote }}
{{- with .Values.branding }}

View file

@ -10,7 +10,6 @@
{{include "cozystack.platform.package.default" (list "cozystack.kubevirt-cdi" $) }}
{{include "cozystack.platform.package.optional.default" (list "cozystack.vm-default-images" $) }}
{{include "cozystack.platform.package.optional.default" (list "cozystack.gpu-operator" $) }}
{{include "cozystack.platform.package.optional.default" (list "cozystack.hami" $) }}
{{include "cozystack.platform.package.default" (list "cozystack.kamaji" $) }}
{{include "cozystack.platform.package.default" (list "cozystack.capi-operator" $) }}
{{include "cozystack.platform.package.default" (list "cozystack.capi-provider-bootstrap-kubeadm" $) }}

View file

@ -105,6 +105,9 @@
{{- /* cozystack-api DaemonSet */ -}}
{{- $apiValues := dict "cozystackAPI" (dict "nodeSelector" $genericNodeSelector) -}}
{{- $_ := set $cozystackEngineComponents "cozystack-api" (dict "values" $apiValues) -}}
{{- /* lineage-controller-webhook DaemonSet */ -}}
{{- $lineageValues := dict "lineageControllerWebhook" (dict "nodeSelector" $genericNodeSelector) -}}
{{- $_ := set $cozystackEngineComponents "lineage-controller-webhook" (dict "values" $lineageValues) -}}
{{- end -}}
{{- if .Values.authentication.oidc.enabled }}
{{include "cozystack.platform.package" (list "cozystack.cozystack-engine" "oidc" $ $cozystackEngineComponents) }}

View file

@ -45,41 +45,6 @@ publishing:
- cdi-uploadproxy
apiServerEndpoint: "" # example: "https://api.example.org"
externalIPs: []
# Exposure mode for the ingress-nginx Service. When "externalIPs" (current
# default) is selected, the Service is created as ClusterIP with
# Service.spec.externalIPs set from publishing.externalIPs. When
# "loadBalancer" is selected, the Service is type: LoadBalancer and a
# CiliumLoadBalancerIPPool makes those same addresses allocatable via LB IPAM.
#
# Service.spec.externalIPs is deprecated upstream in Kubernetes v1.36
# (KEP-5707). The AllowServiceExternalIPs feature gate is expected to default
# to false around v1.40 and the implementation removed around v1.43 — switch
# to "loadBalancer" before upgrading past v1.40.
#
# Caveats for the "loadBalancer" mode:
# - publishing.externalIPs must contain at least one non-empty address,
# otherwise the chart render fails with an explicit error (a LoadBalancer
# Service without a pool would sit in <pending> forever).
# - The ingress-nginx Service is created with externalTrafficPolicy: Local
# to preserve the client source IP. Traffic arriving on a node that does
# not host an ingress-nginx pod is dropped, so the external IP must be
# routed to a node that runs the ingress pod (floating IP / keepalived /
# upstream router / podAntiAffinity).
# - Cilium does NOT announce the IP on its own unless L2 announcements or
# BGP are enabled in the Cilium values (disabled by default in Cozystack).
# This mode assumes the operator already routes the externalIPs to a
# cluster node; enabling announcements is out of scope for this setting.
# - Switching this value on a running cluster causes the ingress-nginx
# Service to be recreated (the HelmRelease has upgrade.force: true and
# the Service kind changes between ClusterIP and LoadBalancer). Expect a
# brief interruption of ingress traffic during the flip.
#
# Scope: this setting only controls the ingress-nginx Service. Other
# cozystack components that currently write Service.spec.externalIPs directly
# (e.g. the vpn app at packages/apps/vpn/templates/service.yaml) are NOT
# migrated by flipping this value and must be addressed separately before
# the AllowServiceExternalIPs feature gate flips to off in ~v1.40.
exposure: externalIPs # "externalIPs" or "loadBalancer"
certificates:
solver: http01 # "http01" or "dns01"
issuerName: letsencrypt-prod

View file

@ -5,6 +5,3 @@ include ../../../hack/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh
test:
helm unittest .

View file

@ -0,0 +1,39 @@
{{- $shouldUpdateCerts := true }}
{{- $configMap := lookup "v1" "ConfigMap" .Release.Namespace "etcd-deployed-version" }}
{{- if $configMap }}
{{- $deployedVersion := index $configMap "data" "version" }}
{{- if $deployedVersion | semverCompare ">= 2.6.1" }}
{{- $shouldUpdateCerts = false }}
{{- end }}
{{- end }}
{{- if $shouldUpdateCerts }}
---
apiVersion: batch/v1
kind: Job
metadata:
name: etcd-hook
annotations:
helm.sh/hook: post-upgrade
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
spec:
template:
metadata:
labels:
policy.cozystack.io/allow-to-apiserver: "true"
spec:
serviceAccountName: etcd-hook
containers:
- name: kubectl
image: docker.io/alpine/k8s:1.33.4
command:
- sh
args:
- -exc
- |-
kubectl --namespace={{ .Release.Namespace }} delete secrets etcd-ca-tls etcd-peer-ca-tls
sleep 10
kubectl --namespace={{ .Release.Namespace }} delete secrets etcd-client-tls etcd-peer-tls etcd-server-tls
kubectl --namespace={{ .Release.Namespace }} delete pods --selector=app.kubernetes.io/instance=etcd,app.kubernetes.io/managed-by=etcd-operator,app.kubernetes.io/name=etcd,cozystack.io/service=etcd
restartPolicy: Never
{{- end }}

View file

@ -0,0 +1,26 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
annotations:
helm.sh/hook: post-upgrade
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
name: etcd-hook
rules:
- apiGroups:
- ""
resources:
- secrets
- pods
verbs:
- get
- list
- watch
- delete
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
- list
- watch

View file

@ -0,0 +1,15 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: etcd-hook
annotations:
helm.sh/hook: post-upgrade
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: etcd-hook
subjects:
- kind: ServiceAccount
name: etcd-hook
namespace: {{ .Release.Namespace | quote }}

View file

@ -0,0 +1,7 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: etcd-hook
annotations:
helm.sh/hook: post-upgrade
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded

View file

@ -0,0 +1,6 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: etcd-deployed-version
data:
version: {{ .Chart.Version }}

View file

@ -1,34 +0,0 @@
suite: etcd chart does not ship a destructive post-upgrade cert-regeneration hook
release:
name: etcd
namespace: tenant-root
templates:
- templates/check-release-name.yaml
- templates/dashboard-resourcemap.yaml
- templates/datastore.yaml
- templates/etcd-defrag.yaml
- templates/hook/job.yaml
- templates/podscrape.yaml
- templates/prometheus-rules.yaml
- templates/version.yaml
tests:
- it: renders no Job named etcd-hook
documentSelector:
path: metadata.name
value: etcd-hook
skipEmptyTemplates: true
asserts:
- hasDocuments:
count: 0
- it: renders no ConfigMap named etcd-deployed-version
documentSelector:
path: metadata.name
value: etcd-deployed-version
skipEmptyTemplates: true
asserts:
- hasDocuments:
count: 0

View file

@ -10,6 +10,3 @@ get-cloudflare-ips:
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh
test:
helm unittest .

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