27 Commits
Author SHA1 Message Date
Ivan Okhotnikov fff9e827d1 fix(operator): address review feedback on private platform source
- Run `go mod tidy` to move github.com/fluxcd/pkg/apis/meta from
  indirect to direct require: it became a direct import after the
  SecretRef type fix.
- Refine the --platform-source-secret flag docstring and the matching
  values.yaml comment: drop identity.pub from the SSH required-keys
  list (Flux source-controller only consumes identity + known_hosts)
  and mention bearerToken as an alternative to username+password for
  Git over HTTPS.
- Note in values.yaml that the Secret must exist in cozy-system before
  the operator reconciles; otherwise source-controller reports a
  failed condition until the Secret is created.

Signed-off-by: Ivan Okhotnikov <ivan.okhotnikov@aenix.io>
2026-06-24 23:22:54 +03:00
IvanHunters 0427b184fa feat(operator): support private OCI registry credentials for platform source
Added --platform-source-secret flag to cozystack-operator that allows
specifying a Secret name for registry authentication. This enables using
private OCI registries and Git repositories as platform sources.

Changes:
- cmd/cozystack-operator/main.go:
  - Add --platform-source-secret flag
  - Pass secretName to generateOCIRepository() and generateGitRepository()
  - Set spec.secretRef in generated resources when secret is specified

- packages/core/installer/values.yaml:
  - Add cozystackOperator.platformSourceSecret parameter

- packages/core/installer/templates/cozystack-operator.yaml:
  - Pass --platform-source-secret flag to operator container

Usage:
1. Create registry credentials secret in cozy-system namespace:
   kubectl create secret docker-registry my-registry-creds \
     --namespace cozy-system \
     --docker-server=registry.example.com \
     --docker-username=user \
     --docker-password=token

2. Configure Helm values:
   cozystackOperator:
     platformSourceUrl: 'oci://registry.example.com/repo/packages'
     platformSourceSecret: 'my-registry-creds'

Closes #2455

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-06-24 22:54:06 +03:00
Myasnikov Daniil c98232a7df style(api,operator): gofmt test case-table struct alignment
gofmt -w on the two touched test files: the case-table struct fields in
rest_helmrelease_spec_test.go and main_test.go were misaligned, and
main_test.go carried a trailing blank line. CI runs no gofmt gate
(pre-commit only runs make generate), so this would otherwise land
unformatted.

Address review feedback from lexfrei.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-06-22 09:48:26 +05:00
Myasnikov Daniil fad709b0c1 refactor(config): lift ParsePositiveDuration and extend ReleaseConfig
Lift parsePositiveDuration from cmd/cozystack-operator/main.go into
pkg/config so cozystack-api can reuse the same startup validator
without duplication. Operator main.go now imports and delegates;
the table-driven test moves to pkg/config/config_test.go.

Extend ReleaseConfig with the five HelmRelease generation knobs
the cozystack-operator already exposes via flags (PR #2509):
HelmReleaseInterval, HelmReleaseRetryInterval,
HelmReleaseInstallTimeout, HelmReleaseUpgradeTimeout,
HelmReleaseMaxHistory. The existing per-Application
HelmInstallTimeout (set from the
release.cozystack.io/helm-install-timeout annotation) keeps its
override semantics - it still wins over the new global
Install/Upgrade timeout defaults when set.

No behaviour change in this commit; the new fields are wired up in
follow-up commits.

Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-06-22 09:48:25 +05:00
Myasnikov Daniil d0e4b4dbe5 refactor(operator): extract parsePositiveDuration into testable helper
The previous closure inside main() couldn't be tested because every error
path called os.Exit(1). Lifting it to a package-level function returning
(time.Duration, error) lets a table test exercise happy paths and rejections
("0s", "-5m", "5x", empty input) directly. main() retains the os.Exit
behaviour via a thin wrapper.

Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-04 22:13:29 +05:00
Myasnikov Daniil 1dc33dff79 fix(operator): reject non-positive HelmRelease duration flags
time.ParseDuration accepts "0s" and negative values, but Flux
HelmRelease.Interval / Install.Timeout / Strategy.RetryInterval require
strictly positive durations to function. Validate at startup so a
misconfigured flag fails fast instead of propagating into every
generated HelmRelease.

Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-04 11:41:35 +05:00
Myasnikov Daniil e410b02e1d feat(operator): expose Install/Upgrade timeouts and MaxHistory as flags
The HelmRelease generation in package_reconciler hardcoded three values
that have caused recurring pain:

- Install.Timeout = 10m (line 214)
- Upgrade.Timeout = 10m (line 220)
- MaxHistory = unset (Helm default 5)

The 10m timeout in particular contradicts the per-Application timeout
work done in `pkg/config/config.go` and
`pkg/registry/apps/application/rest.go` (commit 7b146cbe), which lets
individual Applications override the install/upgrade timeout via
annotation. The PackageSource side had no equivalent — etcd works around
it by hand-rolling its HR YAML in `packages/apps/tenant/templates/etcd.yaml`
to set a 30m timeout, harbor has had multiple commits chasing the same
problem from the other side. This closes that gap.

New operator flags (each maps to a chart value with empty default;
operator uses its own default when value is empty):

- `--helmrelease-install-timeout` (default 10m) → Spec.Install.Timeout
- `--helmrelease-upgrade-timeout` (default 10m) → Spec.Upgrade.Timeout
- `--helmrelease-max-history` (default 5)        → Spec.MaxHistory

Production behaviour unchanged. E2E and edge cases (cert rotation,
slow-installing charts) can override per-cluster without modifying
chart manifests.

Per-component overrides on `ComponentInstall` (mirroring the existing
`UpgradeCRDs` pattern) would be a strictly better fix for charts like
etcd that need an outlier timeout — left as a deliberate followup since
it touches the Package CR API.

Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-04-28 12:44:06 +05:00
Myasnikov Daniil c4c70e0e18 feat(operator): switch HelmRelease retries to Strategy=RetryOnFailure with configurable RetryInterval
The operator generated HelmReleases with `Install.Remediation{Retries: -1}`
and `Upgrade.Remediation{Retries: -1}`, which is the v2 way of saying
"retry forever, never remediate". Combined with the spec-level
`Interval: 5m`, this meant a failed install/upgrade waited a full 5
minutes before the next attempt — even when the underlying race that
caused the failure (e.g. a chart artifact not quite Ready, a HR
dependency reconciling out of order) cleared in seconds.

Audit of the last 30 successful PR runs found that every single run had
its `Install Cozystack` step's `kubectl wait hr/seaweedfs-system` time
out at the 2-minute mark and recover only after an inline
`flux reconcile --force` workaround. Same root cause: 5-minute retry
interval was longer than the test's wait window. That workaround can be
removed once this change lands.

Switch to `Strategy.Name=RetryOnFailure` with `RetryInterval` exposed via
a new `--helmrelease-retry-interval` operator flag (default 30s):

- Functionally equivalent to the previous configuration ("retry forever
  on failure") since `Retries: -1` meant remediation never fired anyway.
- Decouples retry-on-failure timing from `spec.Interval` so failed
  releases recover at 30s without polling healthy releases at the same
  cadence (which would multiply controller load by ~10x for no benefit).
- Helm chart exposes `cozystackOperator.helmReleaseRetryInterval`,
  defaulting to empty (operator uses its own 30s default).
- Same flag pattern as the existing `--helmrelease-interval` (5m default),
  also added in this PR via cherry-pick.

Verified: go build green, go test ./internal/operator/... pass, helm
template renders both flags conditionally on values.

Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-04-28 12:40:13 +05:00
Myasnikov Daniil e197aba625 feat(operator): make HelmRelease Interval configurable, override to 30s in E2E
Adds --helmrelease-interval (default 5m, matching today's behaviour) to the
operator and a corresponding cozystackOperator.helmReleaseInterval value
on the cozy-installer chart (default empty -> no flag rendered ->
operator default 5m applies).

E2E install sets it to 30s. Rationale: with the 5m default, dependency-
blocked HRs (waiting on cert-manager webhooks, CRDs) are requeued only
every 5 min, producing an 8-10 min dead zone in the E2E install where
the fast pack of HRs is Ready but the slow tail hasn't been retried yet.
30s collapses the gap.

Production defaults are unchanged. The override is opt-in per install.

Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-04-28 12:36:28 +05:00
Aleksei SviridkinandClaude 4843a617bc fix(operator): skip PackageSource on empty URL, add comprehensive tests
Guard PackageSource creation on platformSourceURL != "" to avoid
dangling SourceRef when no Flux source resource exists.

Add tests covering all new and modified functions:
- parsePlatformSourceURL (OCI, HTTPS, SSH, empty, unsupported scheme)
- parseRefSpec (single/multi values, whitespace, equals in value,
  missing equals, empty key/value, trailing comma)
- validateOCIRef / validateGitRef (valid keys, invalid keys, format)
- generateOCIRepository / generateGitRepository (with ref, no ref,
  invalid ref)
- installPlatformPackageSource variant valuesFiles validation
- installPlatformPackageSource custom name

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-02-19 18:31:49 +03:00
Aleksei SviridkinandClaude 8b9a11360e fix(operator): skip PackageSource creation when platform source URL is empty
When platformSourceURL is empty no Flux source resource is created, so
creating a PackageSource that references it would leave a dangling
SourceRef in a permanent error state. Guard the creation block on
platformSourceURL != "".

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-02-19 18:29:55 +03:00
Aleksei SviridkinandClaude d0a6ddd782 refactor(operator): reduce variant duplication in installPlatformPackageSource
Replace repetitive Variant struct literals with a loop over variant
data, making it easier to add new variants and reducing copy-paste
errors.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-02-19 18:25:14 +03:00
Aleksei SviridkinandClaude 4821f025fc fix(operator): correct default platformSourceName, gate PackageSource on installCRDs
Change default --platform-source-name from "cozystack-packages" to
"cozystack-platform" to match the value passed by the Helm template.

Gate PackageSource creation on --install-crds flag: when the operator
does not manage CRDs, the PackageSource CRD may not exist yet, so
skip creation and let an external process handle it.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-02-19 18:04:50 +03:00
Aleksei SviridkinandClaude dbfdbc8298 fix(installer): check parsePlatformSourceURL error, wait for PackageSource in E2E
Explicitly check error from parsePlatformSourceURL instead of relying on
the implicit guarantee that installPlatformSourceResource already checked
it. This prevents latent bugs if startup order is ever restructured.

Add wait for platform PackageSource existence in E2E test before creating
Package resource, preventing flaky failures when operator startup is slow.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-02-19 17:57:45 +03:00
Aleksei SviridkinandClaude 58e2b646be fix(operator): use SSA for PackageSource, separate context, unconditional creation
Switch installPlatformPackageSource to server-side apply (SSA) with
field manager, matching the pattern used in crdinstall. SSA is idempotent
and preserves metadata fields managed by other controllers.

Create PackageSource unconditionally (not only when platformSourceURL is
set), matching the previous Helm template behavior where PackageSource
was always created regardless of source URL configuration.

Use a dedicated context with its own 2-minute timeout for PackageSource
creation, separate from the platform source resource installation.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-02-19 17:54:33 +03:00
Aleksei SviridkinandClaude 8450830f06 fix(installer): add CRD wait in E2E, unit tests for PackageSource creation
Add explicit CRD wait (kubectl wait crd --for=condition=Established) in
E2E test before creating Package resources, preventing race condition
between operator CRD installation and resource creation.

Add unit tests for installPlatformPackageSource covering create, update,
and GitRepository sourceRef kind scenarios.

Document that hardcoded variant list is an intentional design choice
matching the previous Helm template behavior.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-02-19 17:50:09 +03:00
Aleksei SviridkinandClaude 0e8b6515af fix(installer): handle parsePlatformSourceURL error, restore variant validation
Check error from parsePlatformSourceURL instead of discarding it, preventing
silent fallback to OCIRepository on invalid URLs.

Move variant validation from deleted packagesource.yaml to
cozystack-operator.yaml template so invalid cozystackOperator.variant
values still fail at helm template/install time.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-02-19 17:42:31 +03:00
Aleksei SviridkinandClaude 655133b81c fix(installer): move PackageSource creation from Helm template to operator
Replace the Helm hook approach with programmatic PackageSource creation
in the operator startup sequence. Helm hooks are unsuitable for persistent
resources like PackageSource because before-hook-creation policy causes
cascade deletion of owned ArtifactGenerators during upgrades.

The operator now creates the platform PackageSource after installing CRDs
and the Flux source resource, using the same create-or-update pattern as
installPlatformSourceResource(). The sourceRef.kind is derived from the
platform source URL (OCIRepository for oci://, GitRepository for git).

Also fix stale comment in e2e test referencing deleted crds/ directory.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-02-19 17:38:23 +03:00
Aleksei SviridkinandClaude 92d261fc1e fix: address review findings in operator and tests
- Remove duplicate "Starting controller manager" log before install
  phases, keep only the one before mgr.Start()
- Rename misleading test "document without kind returns error" to
  "decoder rejects document without kind" to match actual behavior
- Document Helm uninstall CRD behavior in deployment template comment
- Use --health-probe-bind-address=0 consistently with metrics-bind
- Exclude all dotfiles in verify-crds diff, not just .gitattributes

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-02-18 00:49:57 +03:00
Aleksei SviridkinandClaude cecc5861af fix(operator): validate CRD apiVersion, respect SIGTERM during install
- Check both apiVersion and kind when validating embedded CRD manifests
  to prevent applying objects with wrong API group
- Move ctrl.SetupSignalHandler() before install phases so CRD and Flux
  installs respect SIGTERM instead of blocking for up to 2 minutes
- Replace custom contains/searchString helpers with strings.Contains

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-02-18 00:49:56 +03:00
Aleksei SviridkinandClaude 1ddbe68bc2 feat(operator): add --install-crds flag with embedded CRD manifests
Embed Package and PackageSource CRDs in the operator binary using Go
embed, following the same pattern as --install-flux. The operator applies
CRDs at startup using server-side apply, ensuring they are updated on
every operator restart/upgrade.

This addresses the CRD lifecycle concern: Helm crds/ directory handles
initial install, while the operator manages updates on subsequent
deployments.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-02-18 00:49:55 +03:00
Andrei KvapilandClaude af320a86a0 fix(telemetry): use APIReader instead of cached client in operator
The cozystack-operator's manager cache is filtered to only include
specific secrets and namespaces with certain labels. This caused
telemetry collection to fail because resources like kube-system
namespace, nodes, services, and PVs were not in the cache.

Switch to using mgr.GetAPIReader() which bypasses the cache and
queries the API server directly. This is appropriate for telemetry
since it only runs every 15 minutes.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2026-01-27 18:06:30 +01:00
Andrei KvapilandClaude 987a74ae5a refactor(telemetry): split telemetry between operator and controller
Split telemetry collection between cozystack-operator and cozystack-controller:

cozystack-operator now collects cluster-level metrics:
- cozy_cluster_info (cozystack_version, kubernetes_version)
- cozy_nodes_count (os, kernel)
- cozy_cluster_capacity (cpu, memory, nvidia.com/* resources)
- cozy_loadbalancers_count
- cozy_pvs_count (driver, size)
- cozy_package_info (name, variant)

cozystack-controller now collects application-level metrics:
- cozy_application_count (kind) - counts HelmReleases per ApplicationDefinition

Other changes:
- Add pkg/version for build-time version injection via ldflags
- Remove --cozystack-version flag (version now embedded at build time)
- Remove bundle/oidc configuration from telemetry (replaced by package_info)
- Remove cozy_workloads_count metric (replaced by cozy_application_count)

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2026-01-19 13:52:15 +01:00
05b2244b36 feat(operator): add secret replicator and reconciler improvements
Add namespace-based secret replication with label selector approach.
The implementation uses configurable secret name, namespace, and
target namespace selector. Cache filtering optimizes memory usage.

Co-Authored-By: Timofei Larkin <lllamnyp@gmail.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2026-01-06 16:31:40 +01:00
Andrei Kvapil f28d639aea [cozystack-operator] Add Package and PackageSource reconcilers
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-12-30 11:55:21 +01:00
Andrei Kvapil db8a006565 Apply review suggestions
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-12-29 14:38:31 +01:00
Andrei Kvapil 2990f0520a [cozystack-operator] Introduce Cozystack-operator core logic
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-12-25 10:59:48 +01:00