Skip to content

feat(etcd): update operator to v0.4.3 and expose S3 backup schedules - #2428

Merged
Andrei Kvapil (kvaps) merged 16 commits into
mainfrom
feat/etcd-operator-0.4.3-with-backup
May 1, 2026
Merged

Andrei Kvapil (kvaps) merged 16 commits into
mainfrom
feat/etcd-operator-0.4.3-with-backup

Conversation

@lexfrei

@lexfrei Aleksei Sviridkin (lexfrei) commented Apr 20, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does

Bumps the vendored etcd-operator chart from v0.4.2 to v0.4.3 and wires the new backup machinery through the cozystack Etcd app API.

Upstream v0.4.3 ships two new CRDs — EtcdBackup (one-shot snapshot) and EtcdBackupSchedule (periodic snapshots) — along with the EtcdCluster.spec.bootstrap restore field, a new OPERATOR_IMAGE env on the operator deployment, and a replacement of the deprecated gcr.io/kubebuilder/kube-rbac-proxy image. The chart portion of this PR is a clean re-vendor via make update — no local patches.

On top of the bump, packages/extra/etcd exposes a new optional backup.* values block. When backup.enabled=true, the chart renders an EtcdBackupSchedule referencing the platform EtcdCluster/etcd and a Secret/etcd-s3-creds with the S3 credentials expected by the backup-agent. The values field names (destinationPath, endpointURL, s3AccessKey, s3SecretKey, schedule) mirror the existing postgres backup pattern for consistency. The chart fails at template render if any of the four required S3 fields are empty or if destinationPath is malformed (missing s3:// prefix, // segments, no bucket).

Test coverage: 15 helm-unittest cases across tests/backup-secret_test.yaml and tests/etcd-backup-schedule_test.yaml (wired into make test, picked up by hack/helm-unit-tests.sh), plus three bats cases in hack/e2e-apps/etcd.bats covering the baseline Etcd app, the backup: {} default-off path, and the full backup.enabled=true case that verifies the EtcdBackupSchedule materializes and the operator generates the downstream CronJob.

packages/core/platform/sources/etcd-operator.yaml now sets upgradeCRDs: CreateReplace on the etcd-operator component, so Flux applies the new CRDs on upgrade of the chart instead of silently skipping them.

Depends on #2427.

Scope deferred to follow-ups

  • The one-shot EtcdBackup CRD is vendored but not exposed through this chart — users can still hand-apply the CRD directly.
  • EtcdCluster.spec.bootstrap (restore from an existing snapshot) is likewise vendored but not exposed. Both gaps are called out in packages/extra/etcd/README.md.

Release note

feat(etcd): update etcd-operator to v0.4.3 and expose S3 backup schedules on the Etcd app. Set `backup.enabled=true` along with `backup.s3AccessKey`, `backup.s3SecretKey`, `backup.destinationPath`, and `backup.endpointURL` to schedule periodic snapshots.

Summary by CodeRabbit

  • New Features

    • Scheduled S3 backups for etcd clusters (configurable cron, generated S3 credentials) and restore/init from S3 or PVC via new backup resources.
  • Tests

    • New end-to-end tests for etcd lifecycle and backup scenarios; added Helm unit tests for backup templates and validations.
  • Documentation

    • Backup configuration guide and parameter reference added.
  • Chores

    • Operator chart version bump; RBAC, webhooks, and deployment wiring updated; added chart test target.

Upstream release notes: https://github.com/aenix-io/etcd-operator/releases/tag/v0.4.3

Notable changes:
- Add EtcdBackup CRD for declarative etcd snapshots (#307)
- Add EtcdBackupSchedule CRD for scheduled backups (#307)
- Expose metrics port on client service (#299)
- Replace deprecated gcr.io/kubebuilder/kube-rbac-proxy with
  quay.io/brancz/kube-rbac-proxy:v0.18.1 (#308)
- Update Kamaji example manifest

Note: Helm 3 does not install CRDs on upgrade. Existing clusters
require manual application of the new EtcdBackup and
EtcdBackupSchedule CRDs:

  kubectl apply --filename packages/system/etcd-operator/charts/etcd-operator/crds/

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
Expose the EtcdBackupSchedule CRD (etcd.aenix.io/v1alpha1) through
optional values so operators can declaratively configure periodic
S3 backups of the platform etcd cluster. Kamaji stores tenant
control-plane etcd data here, so losing this cluster means losing
every tenant cluster — a backup schedule is critical.

The backup is disabled by default (backup.enabled: false), keeping
existing installations unchanged. When enabled, a Secret is created
with AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY keys referenced by
the new EtcdBackupSchedule resource. Field naming mirrors the
existing postgres backup pattern (destinationPath, endpointURL,
s3AccessKey, s3SecretKey, schedule) for consistency.

Requires etcd-operator v0.4.3+ (shipped in the previous commit).

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
Adds two bats cases: a baseline Etcd managed-app deployment and a
deployment with backup.enabled=true that verifies the
EtcdBackupSchedule resource and the S3 credentials Secret are
materialized. The backup case uses fake S3 credentials — the
operator will not successfully push backups, but the schedule
resource itself is reconciled, which is what this test validates.

Also hardcodes the EtcdBackupSchedule and backup Secret names to
'etcd' / 'etcd-s3-creds' to match the existing hardcoded
EtcdCluster name in the chart (one etcd per tenant namespace).

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
Review found three issues:

1. The guard in backup-secret.yaml only triggered on empty strings,
   not on the placeholder defaults ('<your-access-key>' etc). A user
   enabling backup.enabled=true without overriding the values would
   produce a Secret containing the literal placeholder, and the
   backup CronJob would fail at runtime with an opaque 403 far from
   the source. Add explicit template guards that fail rendering when
   any of the four required fields still hold their placeholder
   default.

2. The backup e2e test waited on .spec.schedule of the newly-created
   EtcdBackupSchedule, which is set at apply time — the wait was
   tautological and did not prove the etcd-operator reconciled
   anything. Replace it with a wait on the EtcdCluster's Ready
   condition plus a poll for the CronJob the operator generates
   from the schedule (selected via the
   etcd.aenix.io/etcdbackupschedule-name label).

3. The backup e2e test also did not wait for EtcdCluster readiness
   before tearing down. Add the same Ready wait as the baseline
   test, and verify the Secret contains the real access key value
   rather than just existing.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
- Mark backup.enabled as optional in the values typedef so
  generated JSON schema no longer lists it in 'required'.
  Previously, 'backup: {}' (relying on the enabled:false default)
  failed schema validation at CRD admission.

- Validate backup.destinationPath has s3:// prefix. Without this
  check, passing a path without the scheme silently used the
  first path segment as the bucket name.

- Add tail newline to values.schema.json to match repo convention
  (cozyvalues-gen output lacks it — worth fixing in that project
  separately).

- Add bats setup() that clears tenant-test etcd resources before
  each test so a failure mid-test does not leak into subsequent
  tests. Hardcoded chart-level resource names ('etcd',
  'etcd-s3-creds') make two Etcd apps in the same namespace
  collide; setup() enforces a single owner per test.

- Add dump_diagnostics() helper that dumps operator logs + the
  relevant CRD state on wait-for-ready timeouts, so CI failures
  surface actionable context instead of bare exit code 124.

- Add a third e2e case 'Create Etcd with empty backup block' that
  asserts backup:{} admits and produces no backup resources — a
  regression test for the required:['enabled'] bug fixed here.

Known scope: the upstream EtcdCluster.spec.bootstrap restore field
(also new in v0.4.3) is not exposed through this values.yaml yet.
Restoring a cluster from an existing snapshot is a follow-up PR.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
- Use name='etcd' in all three e2e cases. The chart pins the Helm
  release name to 'etcd' via check-release-name.yaml, so any other
  name causes template rendering to fail before the HelmRelease
  can reconcile. This is why the tenant chart hardcodes HR name
  'etcd' in packages/apps/tenant/templates/etcd.yaml.

- Consolidate all backup validation guards into
  etcd-backup-schedule.yaml so Helm's alphabetical render order no
  longer determines which failure a user sees first. Previously, a
  bad destinationPath would be masked by the earlier placeholder
  check in backup-secret.yaml.

- Use regex match '^<.*>$' for placeholder detection instead of
  exact string equality. The previous check silently stopped
  firing if anyone updated the default placeholder string in
  values.yaml; the regex form catches any angle-bracketed default.

- Reject destinationPath strings containing '//' after the s3://
  prefix. Previously 's3://bucket//subdir' silently produced a key
  beginning with '/', confusing S3 clients and retention policies.

- Document the backup contract in README.md: what enabling it
  produces, which fields are required, why placeholders are
  rejected, and that restore is a known follow-up gap.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
Fourth review round fixes.

Simplify backup validation by dropping placeholder-regex guards and switching default values of s3AccessKey, s3SecretKey, destinationPath, and endpointURL from angle-bracketed hints to empty strings. A single 'required' check on each field is now the whole validation for presence; the regex guard was brittle (silently stopped matching if the hint ever changed) and dropping it makes the values.yaml defaults match the behavior contract the README describes. s3:// prefix and '//' rejection still live in etcd-backup-schedule.yaml as template-time fail branches.

Add helm-unittest suites in packages/extra/etcd/tests/ covering both templates end-to-end: rendered output assertions for the happy path (bucket/key split, region passthrough, conditional rendering of optional fields) and failedTemplate assertions for every fail branch. A 'test' target is wired into the package Makefile so hack/helm-unit-tests.sh picks it up on 'make unit-tests'.

Add type: Opaque to backup-secret.yaml for consistency with the mongodb template.

E2E: wait for HelmRelease, Secret, and EtcdBackupSchedule deletion in setup() so the empty-backup case does not flake against lingering state from the previous test. Replace the fictitious minio-e2e hostname with no-such-endpoint.invalid and explicitly document that the e2e verifies schedule/CronJob materialization, not backup execution.

Restore via EtcdCluster.spec.bootstrap and the one-shot EtcdBackup CRD are still not exposed through this chart; both are explicit follow-ups called out in the README.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
v0.4.3 introduced new CRDs (EtcdBackup, EtcdBackupSchedule). Flux defaults to Skip on upgrade, so on existing clusters those CRDs never land without a manual kubectl apply.

Setting upgradeCRDs: CreateReplace on the etcd-operator component tells PackageReconciler to wire hr.Spec.Upgrade.Crds accordingly. etcd-operator's CRDs evolve additively between versions, so CreateReplace is safe here.

Depends on the PackageSource.Spec.Variants[].Components[].Install.UpgradeCRDs field — must ship with a cozystack-operator release that recognizes it. Without that, this field is ignored and the HelmRelease still defaults to Skip.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request upgrades the etcd-operator to v0.4.3 and introduces support for scheduled S3 backups within the Cozystack Etcd application. It adds the necessary CRD management, configuration parameters, and validation logic to enable reliable, automated snapshotting of etcd clusters to S3-compatible storage.

Highlights

  • Etcd Operator Upgrade: Updated the vendored etcd-operator chart to v0.4.3 and configured Flux to apply CRD changes using 'CreateReplace'.
  • S3 Backup Integration: Exposed new backup configuration options in the Etcd app, allowing users to schedule periodic snapshots to S3 via the EtcdBackupSchedule CRD.
  • Validation and Testing: Added comprehensive helm-unittest cases and bats e2e tests to ensure backup configurations are correctly rendered and applied.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Ignored Files
  • Ignored by pattern: **/charts/** (7)
    • packages/system/etcd-operator/charts/etcd-operator/Chart.yaml
    • packages/system/etcd-operator/charts/etcd-operator/crds/etcd-backup-schedule.yaml
    • packages/system/etcd-operator/charts/etcd-operator/crds/etcd-backup.yaml
    • packages/system/etcd-operator/charts/etcd-operator/crds/etcd-cluster.yaml
    • packages/system/etcd-operator/charts/etcd-operator/templates/cert-manager/validatingwebhookconfiguration.yml
    • packages/system/etcd-operator/charts/etcd-operator/templates/rbac/clusterrole-manager-role.yml
    • packages/system/etcd-operator/charts/etcd-operator/templates/workload/deployment.yml
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment Gemini (@gemini-code-assist) Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on Gemini (@gemini-code-assist) comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@dosubot dosubot Bot added the kind/feature Categorizes issue or PR as related to a new feature label Apr 20, 2026
@coderabbitai

coderabbitai Bot commented Apr 20, 2026 •

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds scheduled S3 backup support for Etcd: new CRDs (EtcdBackup, EtcdBackupSchedule), Helm templates/values for backup secret and schedule, operator RBAC/webhook and deployment changes, Helm unit and e2e Bats tests, and an etcd-operator version bump to v0.4.3.

Changes

Cohort / File(s) Summary
E2E Tests
hack/e2e-apps/etcd.bats
New Bats suite with setup/cleanup, diagnostics, and three tests covering Etcd creation, disabled backup, and enabled backup schedule behavior.
Operator Installation & Chart Metadata
packages/core/platform/sources/etcd-operator.yaml, packages/system/etcd-operator/charts/etcd-operator/Chart.yaml
Set upgradeCRDs: CreateReplace and bumped operator app/chart version to v0.4.3.
CRDs
packages/system/etcd-operator/charts/etcd-operator/crds/etcd-backup.yaml, .../etcd-backup-schedule.yaml, .../etcd-cluster.yaml
Added EtcdBackup and EtcdBackupSchedule CRDs (S3/PVC destination schemas, status, printer columns); extended EtcdCluster with spec.bootstrap.restore.
Helm templates & values
packages/extra/etcd/templates/backup-secret.yaml, packages/extra/etcd/templates/etcd-backup-schedule.yaml, packages/extra/etcd/values.yaml, packages/extra/etcd/values.schema.json
New templates render etcd-s3-creds Secret and EtcdBackupSchedule when backups enabled; added backup values and JSON schema with validation and defaults.
Helm unit tests & Makefile
packages/extra/etcd/tests/backup-secret_test.yaml, packages/extra/etcd/tests/etcd-backup-schedule_test.yaml, packages/extra/etcd/Makefile
Added positive/negative unit tests for backup templates and a test Makefile target running helm unittest.
Docs
packages/extra/etcd/README.md
Documented backup feature, parameters, S3 destination rules, rendered resources, and chart limitations (restore/one-shot not exposed).
Operator RBAC & Webhooks
packages/system/etcd-operator/charts/etcd-operator/templates/rbac/clusterrole-manager-role.yml, .../templates/cert-manager/validatingwebhookconfiguration.yml
Expanded ClusterRole for batch and new etcd backup CRs; added validating webhooks for EtcdBackup and EtcdBackupSchedule.
Operator Deployment
packages/system/etcd-operator/charts/etcd-operator/templates/workload/deployment.yml
Added OPERATOR_IMAGE env var to the etcd-operator container.
Etcd RD / UI schema
packages/system/etcd-rd/cozyrds/etcd.yaml
Exposed spec.backup in openAPISchema and updated keys ordering for UI to include backup fields.

Sequence Diagram

sequenceDiagram
    participant User as User/Client
    participant Chart as Etcd Helm Chart
    participant K8s as Kubernetes API
    participant Op as Etcd Operator
    participant CJ as CronJob Controller
    participant S3 as S3 Storage

    User->>Chart: Apply Etcd manifest (backup enabled)
    Chart->>K8s: Create Secret `etcd-s3-creds`
    Chart->>K8s: Create EtcdBackupSchedule CR
    Op->>K8s: Reconcile EtcdBackupSchedule -> create CronJob
    CJ->>K8s: Trigger CronJob per schedule -> create Job
    Job->>S3: Upload snapshot to S3
    S3-->>Job: Confirm upload
    Op->>K8s: Update EtcdBackupSchedule status
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

Suggested labels

size:M, lgtm

Suggested reviewers

  • lllamnyp
  • IvanHunters
  • myasnikovdaniil

Poem

🐇 I bounced through charts and YAML fields galore,

secrets snug, schedules set to store,
cron drums tapping, snapshots on their way,
S3 smiles as backups safely stay,
a rabbit cheers — restore dreams to explore!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: updating the etcd-operator to v0.4.3 and exposing S3 backup schedules through the Etcd app.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/etcd-operator-0.4.3-with-backup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces scheduled S3 backup support for the etcd package, adding templates for backup schedules and secrets, updating the etcd-operator configuration, and providing documentation and tests. The review feedback recommends improving the JSON schema by marking mandatory backup fields as required, defining resource requests and limits for the backup workload to ensure stability, and using explicit integer casting in Helm templates for job history limits to avoid type errors.

Comment thread packages/extra/etcd/values.schema.json Outdated
Comment on lines 5 to 61
"backup": {
"description": "Backup configuration.",
"type": "object",
"default": {},
"properties": {
"destinationPath": {
"description": "Destination path for backups (e.g. s3://bucket/path/).",
"type": "string",
"default": ""
},
{
"type": "string"
"enabled": {
"description": "Enable scheduled S3 backups.",
"type": "boolean",
"default": false
},
"endpointURL": {
"description": "S3 endpoint URL for uploads.",
"type": "string",
"default": ""
},
"failedJobsHistoryLimit": {
"description": "Number of failed backup jobs to retain.",
"type": "integer",
"default": 1
},
"forcePathStyle": {
"description": "Use path-style S3 URLs (required for MinIO and most S3-compatible providers).",
"type": "boolean",
"default": true
},
"region": {
"description": "S3 region.",
"type": "string",
"default": ""
},
"s3AccessKey": {
"description": "Access key for S3 authentication.",
"type": "string",
"default": ""
},
"s3SecretKey": {
"description": "Secret key for S3 authentication.",
"type": "string",
"default": ""
},
"schedule": {
"description": "Cron schedule for automated backups.",
"type": "string",
"default": "0 2 * * *"
},
"successfulJobsHistoryLimit": {
"description": "Number of successful backup jobs to retain.",
"type": "integer",
"default": 3
}
],
"x-kubernetes-int-or-string": true
},
"storageClass": {
"description": "StorageClass used to store the data.",
"type": "string",
"default": ""
}
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The backup configuration block contains several fields that are effectively required when backup.enabled is set to true. To improve the user experience in the dashboard and provide earlier validation, consider adding a required array to the backup object schema for destinationPath, endpointURL, s3AccessKey, and s3SecretKey.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch on the UX gap, but it's blocked on the generator rather than a local edit. cozyvalues-gen (which owns values.schema.json for every chart in this repo) only exposes [field]/field for unconditional optional/required and has no annotation for conditional required that tracks backup.enabled. Render-time validation via the Helm required function already produces a clear error, so I've filed the generator feature request and a tracking issue here to revisit once it lands: cozystack/cozyvalues-gen#23, #2486.

Comment on lines +12 to +34
apiVersion: etcd.aenix.io/v1alpha1
kind: EtcdBackupSchedule
metadata:
name: etcd
spec:
clusterRef:
name: etcd
schedule: {{ .Values.backup.schedule | quote }}
successfulJobsHistoryLimit: {{ .Values.backup.successfulJobsHistoryLimit }}
failedJobsHistoryLimit: {{ .Values.backup.failedJobsHistoryLimit }}
destination:
s3:
endpoint: {{ $endpointURL | quote }}
bucket: {{ $bucket | quote }}
{{- with $key }}
key: {{ . | quote }}
{{- end }}
{{- with .Values.backup.region }}
region: {{ . | quote }}
{{- end }}
forcePathStyle: {{ .Values.backup.forcePathStyle }}
credentialsSecretRef:
name: etcd-s3-creds

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The EtcdBackupSchedule workload currently lacks resource requests and limits. Per the project guidelines, new workloads should have resource configurations to ensure cluster stability and predictable scheduling. Consider adding a resources block to the EtcdBackupSchedule spec, potentially mapping it to a new backup.resources value.

References
  1. Missing resource requests/limits on new workloads should be flagged.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The EtcdBackupSchedule CRD shipped by etcd-operator v0.4.3 has no resources or pod-template field — the backup pod spec is produced internally by the operator from a CronJob template it owns (see the CRD in packages/system/etcd-operator/charts/etcd-operator/crds/etcd-backup-schedule.yaml). Adding resources to this manifest would make Helm render an unknown field and the API server would reject it. Resource sizing for the backup pod needs to land upstream in etcd-operator first; I'll revisit once the CRD exposes it.

Comment on lines +20 to +21
successfulJobsHistoryLimit: {{ .Values.backup.successfulJobsHistoryLimit }}
failedJobsHistoryLimit: {{ .Values.backup.failedJobsHistoryLimit }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

While the schema defines these as integers, it is safer to explicitly cast them to integers in the template to avoid potential type mismatch issues if they are passed as strings via Helm's --set flag.

  successfulJobsHistoryLimit: {{ .Values.backup.successfulJobsHistoryLimit | int }}
  failedJobsHistoryLimit: {{ .Values.backup.failedJobsHistoryLimit | int }}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call — applied in aaf9dd0. Both successfulJobsHistoryLimit and failedJobsHistoryLimit now flow through | int in packages/extra/etcd/templates/etcd-backup-schedule.yaml.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/extra/etcd/values.schema.json (1)

1-122: ⚠️ Potential issue | 🟡 Minor

Regenerate to satisfy pre-commit.

Pre-Commit Checks reports this tracked file would be modified by make generate. As per learnings, values.schema.json is auto-generated by cozyvalues-gen from values.yaml; please re-run the generator and commit the result rather than editing by hand.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/extra/etcd/values.schema.json` around lines 1 - 122, The tracked
values.schema.json is auto-generated and must be regenerated from values.yaml
using the cozyvalues-gen generator (run the project generator via make generate
or the cozyvalues-gen command) instead of manual edits; re-run make generate (or
cozyvalues-gen pointed at packages/extra/etcd/values.yaml), commit the updated
values.schema.json, and ensure pre-commit passes so the file no longer appears
modified by the generator.
🧹 Nitpick comments (1)
hack/e2e-apps/etcd.bats (1)

61-67: Consider a short settle window before asserting absence.

After wait hr etcd --for=condition=ready, the HelmRelease is reconciled, so with backup.enabled=false the schedule/secret will indeed never be rendered — the assertions are correct. However, if a prior test left an etcd-s3-creds Secret or EtcdBackupSchedule behind and the setup() --for=delete timed out silently (both use || true), these get checks could pass or fail non-deterministically on a re-run. A quick kubectl get ... --ignore-not-found plus an explicit emptiness assert, or tightening setup() to not swallow timeout failures for these specific resources, would make the negative assertion more robust.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hack/e2e-apps/etcd.bats` around lines 61 - 67, The negative assertions for
etcdbackupschedule.etcd.aenix.io (EtcdBackupSchedule) and secret etcd-s3-creds
are flakey because prior artifacts can persist; add a short settle/check step
after waiting for HelmRelease readiness in hack/e2e-apps/etcd.bats: either (A)
run kubectl get secret etcd-s3-creds --ignore-not-found and kubectl get
etcdbackupschedule.etcd.aenix.io etcd --ignore-not-found and explicitly assert
the outputs are empty, or (B) tighten setup() so the prior --for=delete calls do
not swallow timeouts for these two resources, ensuring any leftover
Secret/EtcdBackupSchedule is removed before asserting absence; update the test
to use one of these approaches so the negative checks against etcd-s3-creds and
etcdbackupschedule.etcd.aenix.io are deterministic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@hack/e2e-apps/etcd.bats`:
- Around line 17-22: The diagnostic function dump_diagnostics currently uses the
wrong redirection order (e.g., "2>&1 >&3") which causes stderr to be sent to the
test's default stdout instead of fd 3; update each kubectl invocation inside
dump_diagnostics (the lines calling kubectl -n tenant-test get ..., kubectl -n
tenant-test describe ..., and kubectl -n cozy-etcd-operator logs ...) to
redirect stdout to fd 3 first and then stderr to follow (i.e., change the order
so stdout goes to >&3 and then stderr is redirected to that fd).

In `@packages/extra/etcd/README.md`:
- Line 9: The manually-added "Backups" prose in packages/extra/etcd/README.md
will be lost on regeneration; move that guidance into the chart's values.yaml
using the `@section` pattern and inline comments (follow the example in
packages/apps/postgres/values.yaml) so it persists when cozyvalues-gen runs, and
update wording to use "custom resource manifest" instead of "CRD" for EtcdBackup
and any references to EtcdCluster.spec.bootstrap; ensure the values.yaml entry
documents that restore/one-shot backup requires applying the EtcdBackup custom
resource instance and where to find the manifest/template.

In `@packages/system/etcd-rd/cozyrds/etcd.yaml`:
- Line 11: The committed openAPISchema in etcd.yaml is out of sync with the
generated schema; run the generator and commit the result: run make generate
(which uses cozyvalues-gen to regenerate the embedded openAPISchema from
packages/extra/etcd/values.yaml and values.schema.json), verify the diff updates
packages/system/etcd-rd/cozyrds/etcd.yaml, and commit the regenerated file so
the openAPISchema in etcd.yaml matches the generated output.

---

Outside diff comments:
In `@packages/extra/etcd/values.schema.json`:
- Around line 1-122: The tracked values.schema.json is auto-generated and must
be regenerated from values.yaml using the cozyvalues-gen generator (run the
project generator via make generate or the cozyvalues-gen command) instead of
manual edits; re-run make generate (or cozyvalues-gen pointed at
packages/extra/etcd/values.yaml), commit the updated values.schema.json, and
ensure pre-commit passes so the file no longer appears modified by the
generator.

---

Nitpick comments:
In `@hack/e2e-apps/etcd.bats`:
- Around line 61-67: The negative assertions for
etcdbackupschedule.etcd.aenix.io (EtcdBackupSchedule) and secret etcd-s3-creds
are flakey because prior artifacts can persist; add a short settle/check step
after waiting for HelmRelease readiness in hack/e2e-apps/etcd.bats: either (A)
run kubectl get secret etcd-s3-creds --ignore-not-found and kubectl get
etcdbackupschedule.etcd.aenix.io etcd --ignore-not-found and explicitly assert
the outputs are empty, or (B) tighten setup() so the prior --for=delete calls do
not swallow timeouts for these two resources, ensuring any leftover
Secret/EtcdBackupSchedule is removed before asserting absence; update the test
to use one of these approaches so the negative checks against etcd-s3-creds and
etcdbackupschedule.etcd.aenix.io are deterministic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3d067dff-ec2a-40f4-b59b-3bd734d94694

📥 Commits

Reviewing files that changed from the base of the PR and between 171c793 and 6deca1b.

📒 Files selected for processing (18)
  • hack/e2e-apps/etcd.bats
  • packages/core/platform/sources/etcd-operator.yaml
  • packages/extra/etcd/Makefile
  • packages/extra/etcd/README.md
  • packages/extra/etcd/templates/backup-secret.yaml
  • packages/extra/etcd/templates/etcd-backup-schedule.yaml
  • packages/extra/etcd/tests/backup-secret_test.yaml
  • packages/extra/etcd/tests/etcd-backup-schedule_test.yaml
  • packages/extra/etcd/values.schema.json
  • packages/extra/etcd/values.yaml
  • packages/system/etcd-operator/charts/etcd-operator/Chart.yaml
  • packages/system/etcd-operator/charts/etcd-operator/crds/etcd-backup-schedule.yaml
  • packages/system/etcd-operator/charts/etcd-operator/crds/etcd-backup.yaml
  • packages/system/etcd-operator/charts/etcd-operator/crds/etcd-cluster.yaml
  • packages/system/etcd-operator/charts/etcd-operator/templates/cert-manager/validatingwebhookconfiguration.yml
  • packages/system/etcd-operator/charts/etcd-operator/templates/rbac/clusterrole-manager-role.yml
  • packages/system/etcd-operator/charts/etcd-operator/templates/workload/deployment.yml
  • packages/system/etcd-rd/cozyrds/etcd.yaml

Comment thread hack/e2e-apps/etcd.bats
Comment thread packages/extra/etcd/README.md Outdated
Comment thread packages/system/etcd-rd/cozyrds/etcd.yaml Outdated
Run 'make generate' under packages/extra/etcd to refresh the JSON schema
and the corresponding ResourceDefinition YAML after adding backup and
resources fields. Matches cozyvalues-gen v1.3.0 output used by the
pre-commit CI check.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
cozytest.sh does not implement the bats 'run <cmd>' helper and '$status'
variable, so the 'Create Etcd with empty backup block (disabled by
default)' test died with 'run: not found' on every attempt. Replace the
bats idiom with plain shell: use '!' prefix and redirect stderr to
assert the resources absent-when-backup-disabled invariant directly.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
hack/e2e-apps/etcd.bats (1)

19-21: ⚠️ Potential issue | 🟡 Minor

Keep stderr on fd 3 for diagnostics.

This is the same redirection issue previously noted: 2>&1 >&3 sends stderr to the original stdout before stdout is moved to fd 3. Use >&3 2>&1 so kubectl errors appear in the Bats diagnostic stream.

🛠️ Proposed fix
-  kubectl -n tenant-test get etcdcluster,etcdbackupschedule,cronjob -o wide 2>&1 >&3 || true
-  kubectl -n tenant-test describe etcdbackupschedule etcd 2>&1 >&3 || true
-  kubectl -n cozy-etcd-operator logs -l app.kubernetes.io/name=etcd-operator --tail=100 2>&1 >&3 || true
+  kubectl -n tenant-test get etcdcluster,etcdbackupschedule,cronjob -o wide >&3 2>&1 || true
+  kubectl -n tenant-test describe etcdbackupschedule etcd >&3 2>&1 || true
+  kubectl -n cozy-etcd-operator logs -l app.kubernetes.io/name=etcd-operator --tail=100 >&3 2>&1 || true
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hack/e2e-apps/etcd.bats` around lines 19 - 21, The three kubectl invocations
in hack/e2e-apps/etcd.bats (the "kubectl -n tenant-test get
etcdcluster,etcdbackupschedule,cronjob -o wide", the "kubectl -n tenant-test
describe etcdbackupschedule etcd", and the "kubectl -n cozy-etcd-operator logs
-l app.kubernetes.io/name=etcd-operator --tail=100" lines) currently use the
wrong redirection ordering; change the order so stdout is duplicated to file
descriptor 3 first and then stderr is redirected to stdout (so that stderr ends
up on fd 3) to ensure kubectl errors are captured by the Bats diagnostic stream.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@hack/e2e-apps/etcd.bats`:
- Around line 8-15: The setup() function currently swallows cleanup errors with
"|| true" on kubectl delete/wait commands (kubectl -n tenant-test delete
etcd.apps.cozystack.io --all --ignore-not-found --timeout=2m, kubectl -n
tenant-test wait hr/etcd --for=delete ..., kubectl -n tenant-test wait
secret/etcd-s3-creds ..., kubectl -n tenant-test wait
etcdbackupschedule.etcd.aenix.io/etcd ...), which masks teardown failures;
either remove the "|| true" so failures bubble and fail the test setup, or move
these cleanup calls out of setup() into the framework/global teardown path
(ensuring the same resource names are cleaned there) so per-test setup does not
hide errors.
- Around line 83-89: Update the schedule cron expression in the etcd E2E test so
backup Jobs won't run during the ~365s test window: replace the high-frequency
schedule value for the "schedule" key (found next to destinationPath and
endpointURL in hack/e2e-apps/etcd.bats) with a low-frequency expression (e.g., a
daily or weekly cron like "0 0 * * *" or "@weekly") for both occurrences
referenced in the diff so the test only verifies CronJob rendering and does not
trigger actual backup Jobs.
- Around line 39-40: Replace the brittle "sleep 5" + "kubectl -n tenant-test
wait hr etcd --for=condition=ready" pattern with a polling helper that first
checks for the HelmRelease's existence (e.g., wait_for_hr_exists namespace name)
and only then calls "kubectl ... wait ... --for=condition=ready"; update all
occurrences where "sleep 5" and "kubectl -n tenant-test wait hr etcd
--timeout=60s --for=condition=ready || { dump_diagnostics; false; }" appear
(lines around the three occurrences) to call this new helper, ensuring the
helper uses a loop with a timeout and exits to dump_diagnostics on failure.

---

Duplicate comments:
In `@hack/e2e-apps/etcd.bats`:
- Around line 19-21: The three kubectl invocations in hack/e2e-apps/etcd.bats
(the "kubectl -n tenant-test get etcdcluster,etcdbackupschedule,cronjob -o
wide", the "kubectl -n tenant-test describe etcdbackupschedule etcd", and the
"kubectl -n cozy-etcd-operator logs -l app.kubernetes.io/name=etcd-operator
--tail=100" lines) currently use the wrong redirection ordering; change the
order so stdout is duplicated to file descriptor 3 first and then stderr is
redirected to stdout (so that stderr ends up on fd 3) to ensure kubectl errors
are captured by the Bats diagnostic stream.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f8150982-1e6b-4c4e-9345-0b983afc8e93

📥 Commits

Reviewing files that changed from the base of the PR and between 9fc99ee and 4d7b4bb.

📒 Files selected for processing (1)
  • hack/e2e-apps/etcd.bats

Comment thread hack/e2e-apps/etcd.bats
Comment thread hack/e2e-apps/etcd.bats Outdated
Comment thread hack/e2e-apps/etcd.bats Outdated
Address review feedback from gemini-code-assist on
packages/extra/etcd/templates/etcd-backup-schedule.yaml:20-21: explicitly
coerce successfulJobsHistoryLimit and failedJobsHistoryLimit to int so
string values passed via helm --set do not produce a type mismatch in
the EtcdBackupSchedule spec.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
Address review feedback from coderabbitai on
hack/e2e-apps/etcd.bats:19-21: redirect stdout to fd 3 first, then dup
stderr onto the new fd 1 so failures from kubectl (NotFound, RBAC,
timeouts) reach the test diagnostics stream instead of being swallowed
by the bats-captured default stdout.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
Address review feedback from coderabbitai on
packages/extra/etcd/README.md:9: CRDs are schema definitions; what users
apply to trigger a restore or one-shot backup is a custom resource
instance of the EtcdBackup kind, not the CRD itself. Reword accordingly.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
Address review feedback from coderabbitai on
hack/e2e-apps/etcd.bats:9-14: drop the '|| true' guards in setup() so
real teardown failures (RBAC errors, stuck finalizers, wait timeouts)
fail the test setup instead of letting the next test run against stale
HelmRelease, Secret, or EtcdBackupSchedule state. --ignore-not-found
still covers the empty-namespace case on a clean run.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
…diness

Address review feedback from coderabbitai on
hack/e2e-apps/etcd.bats:39-40: replace the sleep-then-wait pattern with
a wait_etcd_hr_ready helper that polls hr/etcd until the Cozystack API
reconciles the Etcd CR and only then waits for condition=ready.
kubectl wait exits immediately when the resource does not yet exist,
which caused spurious failures when HelmRelease creation took longer
than 5s.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
Address review feedback from coderabbitai on
hack/e2e-apps/etcd.bats:89: the test verifies that the chart renders
EtcdBackupSchedule and that the etcd-operator materializes a CronJob,
not that backups fire. Use "0 0 1 1 *" so no Jobs run against the
intentionally invalid endpoint during the ~6-minute test window.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
hack/e2e-apps/etcd.bats (1)

69-70: Negated existence check can silently pass on transient API errors.

! kubectl get ... 2>/dev/null treats any non-zero exit (NotFound, but also RBAC/API errors, unreachable apiserver, missing CRD) as "resource absent" and passes the test. In this suite the HR is ready and the EtcdBackupSchedule CRD is guaranteed, so it's not an actionable bug today — just note that switching to a NotFound-specific check (e.g. grepping the error, or kubectl get ... -o name | wc -l equal to 0) would harden it against future regressions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hack/e2e-apps/etcd.bats` around lines 69 - 70, The negated checks using "!
kubectl -n tenant-test get etcdbackupschedule.etcd.aenix.io etcd 2>/dev/null"
and the secret check can silently pass on transient API errors; change them to
explicitly detect NotFound by running the kubectl get for
"etcdbackupschedule.etcd.aenix.io etcd" and for "secret etcd-s3-creds", capture
stdout/stderr, and assert absence only when the error text contains "NotFound"
(or use a count-based check like `kubectl ... -o name | wc -l` equals 0) so
RBAC/API/unreachable errors do not falsely mark the resource as absent. Ensure
you update both occurrences referring to the etcdbackupschedule resource and the
etcd-s3-creds secret.
packages/extra/etcd/templates/etcd-backup-schedule.yaml (1)

32-32: Optional: guard forcePathStyle against a nil override.

{{ .Values.backup.forcePathStyle }} emits an empty scalar (forcePathStyle: ) if the value is ever explicitly overridden to null. The chart's values.yaml/schema default (true) keeps this safe today, but a small defensive change avoids producing invalid YAML should that default change or a user pass forcePathStyle: null:

Proposed defensive render
-      forcePathStyle: {{ .Values.backup.forcePathStyle }}
+      forcePathStyle: {{ .Values.backup.forcePathStyle | default false }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/extra/etcd/templates/etcd-backup-schedule.yaml` at line 32, The
template currently renders forcePathStyle directly which can emit an empty
scalar when overridden to null; change the rendering to use Helm's default
function so null falls back to the safe boolean (e.g. replace the current
interpolation of .Values.backup.forcePathStyle with {{ default true
.Values.backup.forcePathStyle }}), keeping the YAML key name forcePathStyle
as-is so the template always emits a valid true/false value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@hack/e2e-apps/etcd.bats`:
- Around line 69-70: The negated checks using "! kubectl -n tenant-test get
etcdbackupschedule.etcd.aenix.io etcd 2>/dev/null" and the secret check can
silently pass on transient API errors; change them to explicitly detect NotFound
by running the kubectl get for "etcdbackupschedule.etcd.aenix.io etcd" and for
"secret etcd-s3-creds", capture stdout/stderr, and assert absence only when the
error text contains "NotFound" (or use a count-based check like `kubectl ... -o
name | wc -l` equals 0) so RBAC/API/unreachable errors do not falsely mark the
resource as absent. Ensure you update both occurrences referring to the
etcdbackupschedule resource and the etcd-s3-creds secret.

In `@packages/extra/etcd/templates/etcd-backup-schedule.yaml`:
- Line 32: The template currently renders forcePathStyle directly which can emit
an empty scalar when overridden to null; change the rendering to use Helm's
default function so null falls back to the safe boolean (e.g. replace the
current interpolation of .Values.backup.forcePathStyle with {{ default true
.Values.backup.forcePathStyle }}), keeping the YAML key name forcePathStyle
as-is so the template always emits a valid true/false value.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8b11b0d3-98e4-4e5b-9e93-639c68101701

📥 Commits

Reviewing files that changed from the base of the PR and between 4d7b4bb and cc7d09b.

📒 Files selected for processing (3)
  • hack/e2e-apps/etcd.bats
  • packages/extra/etcd/README.md
  • packages/extra/etcd/templates/etcd-backup-schedule.yaml
✅ Files skipped from review due to trivial changes (1)
  • packages/extra/etcd/README.md

@kvaps
Andrei Kvapil (kvaps) merged commit b5a1ae5 into main May 1, 2026
7 checks passed
@kvaps
Andrei Kvapil (kvaps) deleted the feat/etcd-operator-0.4.3-with-backup branch May 1, 2026 11:16
Aleksei Sviridkin (lexfrei) added a commit to cozystack/website that referenced this pull request May 4, 2026
## What this PR does

Bumps the pinned etcd-operator version in the v1.2 platform-stack guide
from `v0.4.2` to `v0.4.3`.

Accompanies cozystack/cozystack#2428, which re-vendors the chart and
adds the `backup.*` API surface on the managed etcd app. The new backup
parameters will land in
`content/en/docs/v1.2/operations/services/etcd.md` automatically when
the release-tag sync job pulls the updated
`packages/extra/etcd/README.md` — no hand-edited doc is needed here.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Documentation**
* Updated Kubernetes tenant etcd deployment docs to reference the
operator component v0.4.3 (was v0.4.2).
* Clarified wording by renaming the bullet prefix from “Operator” to
“Kubernetes operator” for clearer architecture guidance.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
myasnikovdaniil pushed a commit to tym83/cozystack-website that referenced this pull request May 18, 2026
Accompanies cozystack/cozystack#2428 which updates the vendored chart and adds the S3 backup schedule API surface.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/feature Categorizes issue or PR as related to a new feature size/L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants