Skip to content

feat (backup) : add option to conditionally copy registry auth secret from operator namespace to workspace namespace for backup/restore#1618

Open
rohanKanojia wants to merge 2 commits intodevfile:mainfrom
rohankanojia-forks:pr/secret-fallback-option
Open

feat (backup) : add option to conditionally copy registry auth secret from operator namespace to workspace namespace for backup/restore#1618
rohanKanojia wants to merge 2 commits intodevfile:mainfrom
rohankanojia-forks:pr/secret-fallback-option

Conversation

@rohanKanojia
Copy link
Copy Markdown
Member

@rohanKanojia rohanKanojia commented May 5, 2026

What does this PR do?

Adds a new copyOperatorAuthSecret configuration flag to control whether the DevWorkspace Operator automatically copies registry authentication secrets from the operator namespace to workspace namespaces for backup/restore operations.

Breaking Change: The default value for copyOperatorAuthSecret is false (was implicitlytrue before).

  • Adds copyOperatorAuthSecret field to RegistryConfig API (defaults to false)
  • Refactors CopySecret() to respect the flag and never overwrite user-provided secrets
  • When copyOperatorAuthSecret: false (default), the operator will not copy secrets and returns a clear error message instructing users to create their own scoped credentials
  • When copyOperatorAuthSecret: true , maintains existing behavior - automatically copies operator's secret to workspace namespace if not found

What issues does this PR fix or reference?

https://redhat.atlassian.net/browse/CRW-10758

Is it tested? How?

Scenario 1: Default Behavior `copyOperatorAuthSecret` not set (defaults to `false`)
  1. Configure DWOC:
kubectl apply -f - <<EOF
apiVersion: controller.devfile.io/v1alpha1
kind: DevWorkspaceOperatorConfig
metadata:
  name: devworkspace-operator-config
  namespace: openshift-operators
config:
  workspace:
    backupCronJob:
      enable: true
      schedule: "* * * * *"
      registry:
        path: "quay.io/rokumar"
        authSecret: "quay-push-secret"
        # copyOperatorAuthSecret is not set (should default to false)
EOF
  1. Create test namespace
oc new-project test-workspace
  1. Create a DevWorkspace
kubectl apply -f - <<EOF
apiVersion: workspace.devfile.io/v1alpha2
kind: DevWorkspace
metadata:
  name: test-workspace-default
  namespace: test-workspace
spec:
  started: true
  template:
    projects:
      - name: web-nodejs-sample
        git:
          remotes:
            origin: "https://github.com/che-samples/web-nodejs-sample.git"
    components:
      - name: dev
        container:
          image: quay.io/devfile/universal-developer-image:latest
          memoryLimit: 512Mi
          memoryRequest: 256Mi
          cpuRequest: 1000m
    commands:
      - id: say-hello
        exec:
          component: dev
          commandLine: echo "Hello from $(pwd)"
          workingDir: ${PROJECT_SOURCE}/app
EOF
  1. Wait for workspace to become running
  2. Stop devworkspace:
kubectl patch devworkspace test-workspace-default -n test-workspace \
  --type merge -p '{"spec":{"started":false}}'
  1. Check for operator logs, it should show that operator logs error that secret is not present:
# Get the logs from the controller manager
oc logs -lapp.kubernetes.io/name=devworkspace-controller -nopenshift-operators -f

{"level":"error","ts":"2026-05-05T04:54:00Z","logger":"controllers.BackupCronJob","msg":"Failed to handle registry auth secret for DevWorkspace","devworkspace":"test-workspace-default","error":"registry auth secret \"devworkspace-backup-registry-auth\" not found in workspace namespace \"test-workspace\". copyOperatorAuthSecret is set to false, so the operator will not copy the secret. Please manually create a secret of type kubernetes.io/dockerconfigjson with the name \"devworkspace-backup-registry-auth\" in the workspace namespace","stacktrace":"github.com/devfile/devworkspace-operator/controllers/backupcronjob.(*BackupCronJobReconciler).createBackupJob\n\t/devworkspace-operator/controllers/backupcronjob/backupcronjob_controller.go:350\ngithub.com/devfile/devworkspace-operator/controllers/backupcronjob.(*BackupCronJobReconciler).executeBackupSync\n\t/devworkspace-operator/controllers/backupcronjob/backupcronjob_controller.go:253\ngithub.com/devfile/devworkspace-operator/controllers/backupcronjob.(*BackupCronJobReconciler).startCron.func1\n\t/devworkspace-operator/controllers/backupcronjob/backupcronjob_controller.go:194\ngithub.com/robfig/cron/v3.FuncJob.Run\n\t/devworkspace-operator/vendor/github.com/robfig/cron/v3/cron.go:131\ngithub.com/robfig/cron/v3.(*Cron).startJob.func1\n\t/devworkspace-operator/vendor/github.com/robfig/cron/v3/cron.go:307"}
Scenario 2: Explicit Secret Copying with copyOperatorAuthSecret: true
  1. Before the operator can copy the secret, it must exist where the operator is running.
kubectl create secret docker-registry quay-push-secret \ --docker-server=quay.io \ --docker-username=<your-username> \ --docker-password=<your-token> \ -n openshift-operators
  1. Configure DWOC with the copy flag enabled:
kubectl apply -f - <<EOF
apiVersion: controller.devfile.io/v1alpha1
kind: DevWorkspaceOperatorConfig
metadata:
  name: devworkspace-operator-config
  namespace: openshift-operators
config:
  workspace:
    backupCronJob:
      enable: true
      schedule: "* * * * *"
      registry:
        path: "quay.io/rokumar"
        authSecret: "quay-push-secret"
        copyOperatorAuthSecret: true  # Explicitly enabled
EOF
  1. Create/Ensure test namespace
oc new-project test-workspace-success 
  1. Create a DevWorkspace
kubectl apply -f - <<EOF
apiVersion: workspace.devfile.io/v1alpha2
kind: DevWorkspace
metadata:
  name: test-workspace-copy-true
  namespace: test-workspace-success
spec:
  started: true
  template:
    projects:
      - name: web-nodejs-sample
        git:
          remotes:
            origin: "https://github.com/che-samples/web-nodejs-sample.git"
    components:
      - name: dev
        container:
          image: quay.io/devfile/universal-developer-image:latest
EOF
  1. Wait for workspace to become running, then Stop it:
kubectl patch devworkspace test-workspace-copy-true -n test-workspace-success \
  --type merge -p '{"spec":{"started":false}}'
  1. Wait for backup to be triggered and Verify the Secret was copied automatically:
kubectl get secret devworkspace-backup-registry-auth -n test-workspace-success

# Expected Result: ✅ Secret devworkspace-backup-registry-auth exists.
Scenario 3 : User Provides Secret (copyOperatorAuthSecret: false)
  1. Configure DWOC with copyOperatorAuthSecret = false
kubectl apply -f - <<EOF
apiVersion: controller.devfile.io/v1alpha1
kind: DevWorkspaceOperatorConfig
metadata:
  name: devworkspace-operator-config
  namespace: openshift-operators
config:
  workspace:
    backupCronJob:
      enable: true
      schedule: "* * * * *"
      registry:
        path: "quay.io/rokumar"
        authSecret: "quay-push-secret"
        copyOperatorAuthSecret: false
EOF
  1. Create/Ensure test namespace
oc new-project test-workspace-secret-already-present 
  1. Manually create the secret in the user namespace (note down resourceVersion) :
kubectl create secret docker-registry devworkspace-backup-registry-auth  --docker-server=quay.io --docker-username=$QUAY_USERNAME  --docker-password=$QUAY_PASSWORD

kubectl label secret devworkspace-backup-registry-auth    [controller.devfile.io/watch-secret=true](http://controller.devfile.io/watch-secret=true)

kubectl get secret devworkspace-backup-registry-auth -o jsonpath='{.metadata.resourceVersion}'
  1. Create a DevWorkspace
kubectl apply -f - <<EOF
apiVersion: workspace.devfile.io/v1alpha2
kind: DevWorkspace
metadata:
  name: test-workspace-copy-true
spec:
  started: true
  template:
    projects:
      - name: web-nodejs-sample
        git:
          remotes:
            origin: "https://github.com/che-samples/web-nodejs-sample.git"
    components:
      - name: dev
        container:
          image: quay.io/devfile/universal-developer-image:latest
EOF
  1. Wait for workspace to become running, then Stop it:
kubectl patch devworkspace test-workspace-copy-true \
  --type merge -p '{"spec":{"started":false}}'
  1. Verify Backup should be successful. And Secret resourceVersion stays same as old noted version:
# Should be same as step 4
kubectl get secret devworkspace-backup-registry-auth -o jsonpath='{.metadata.resourceVersion}'
Scenario 4: copyOperatorAuthSecret is false and no manual secret exists.
  1. Configure DWOC with copyOperatorAuthSecret = false
kubectl apply -f - <<EOF
apiVersion: controller.devfile.io/v1alpha1
kind: DevWorkspaceOperatorConfig
metadata:
  name: devworkspace-operator-config
  namespace: openshift-operators
config:
  workspace:
    backupCronJob:
      enable: true
      schedule: "* * * * *"
      registry:
        path: "quay.io/rokumar"
        authSecret: "quay-push-secret"
        copyOperatorAuthSecret: false
EOF
  1. Create/Ensure test namespace
oc new-project test-workspace-secret-absent
  1. Ensure no secret already present in namespace:
kubectl delete secret devworkspace-backup-registry-auth --ignore-not-found
  1. Create a DevWorkspace
kubectl apply -f - <<EOF
apiVersion: workspace.devfile.io/v1alpha2
kind: DevWorkspace
metadata:
  name: test-workspace-copy-true
spec:
  started: true
  template:
    projects:
      - name: web-nodejs-sample
        git:
          remotes:
            origin: "https://github.com/che-samples/web-nodejs-sample.git"
    components:
      - name: dev
        container:
          image: quay.io/devfile/universal-developer-image:latest
EOF
  1. Wait for workspace to become running, then Stop it:
kubectl patch devworkspace test-workspace-copy-true \
  --type merge -p '{"spec":{"started":false}}'
  1. Check for operator logs, it should show that operator logs error that secret is not present:
# Get the logs from the controller manager
oc logs -lapp.kubernetes.io/name=devworkspace-controller -nopenshift-operators -f

{"level":"error","ts":"2026-05-05T10:47:00Z","logger":"controllers.BackupCronJob","msg":"Failed to handle registry auth secret for DevWorkspace","devworkspace":"test-workspace-copy-true","error":"registry auth secret \"devworkspace-backup-registry-auth\" not found in workspace namespace \"test-workspace-secret-absent\". copyOperatorAuthSecret is set to false, so the operator will not copy the secret. Please manually create a secret of type kubernetes.io/dockerconfigjson with the name \"devworkspace-backup-registry-auth\" in the workspace namespace","stacktrace":"github.com/devfile/devworkspace-operator/controllers/backupcronjob.(*BackupCronJobReconciler).createBackupJob\n\t/devworkspace-operator/controllers/backupcronjob/backupcronjob_controller.go:350\ngithub.com/devfile/devworkspace-operator/controllers/backupcronjob.(*BackupCronJobReconciler).executeBackupSync\n\t/devworkspace-operator/controllers/backupcronjob/backupcronjob_controller.go:253\ngithub.com/devfile/devworkspace-operator/controllers/backupcronjob.(*BackupCronJobReconciler).startCron.func1\n\t/devworkspace-operator/controllers/backupcronjob/backupcronjob_controller.go:194\ngithub.com/robfig/cron/v3.FuncJob.Run\n\t/devworkspace-operator/vendor/github.com/robfig/cron/v3/cron.go:131\ngithub.com/robfig/cron/v3.(*Cron).startJob.func1\n\t/devworkspace-operator/vendor/github.com/robfig/cron/v3/cron.go:307"}

PR Checklist

  • E2E tests pass (when PR is ready, comment /test v8-devworkspace-operator-e2e, v8-che-happy-path to trigger)
    • v8-devworkspace-operator-e2e: DevWorkspace e2e test
    • v8-che-happy-path: Happy path for verification integration with Che

Summary by CodeRabbit

Release Notes

  • New Features

    • Added copyOperatorAuthSecret configuration option to control whether the operator automatically copies authentication secrets from the operator namespace to workspace namespaces when workspace-level secrets are unavailable.
  • Bug Fixes

    • Enhanced safeguards to ensure existing workspace authentication secrets are never overwritten; user-provided secrets are always respected.
  • Documentation

    • Expanded authentication secret documentation to clarify copying behavior, conditions, and protections when workspace-level secrets are missing.

@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented May 5, 2026

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 5, 2026

Warning

Rate limit exceeded

@rohanKanojia has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 22 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 01d41024-aedc-4d56-bec1-bbe731231525

📥 Commits

Reviewing files that changed from the base of the PR and between 87f720c and 0f8ddda.

📒 Files selected for processing (12)
  • apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go
  • apis/controller/v1alpha1/zz_generated.deepcopy.go
  • controllers/backupcronjob/backupcronjob_controller_test.go
  • deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml
  • deploy/deployment/kubernetes/combined.yaml
  • deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml
  • deploy/deployment/openshift/combined.yaml
  • deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml
  • deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml
  • pkg/config/sync.go
  • pkg/secrets/backup.go
  • pkg/secrets/backup_test.go
📝 Walkthrough

Walkthrough

This PR adds a new copyOperatorAuthSecret boolean field to RegistryConfig that controls whether the operator copies Docker authentication secrets from the operator namespace into workspace namespaces when missing. The field is propagated through type definitions, deepcopy logic, config merging, and secret handling logic, with corresponding CRD schema updates and test coverage.

Changes

Registry Auth Secret Copy Control

Layer / File(s) Summary
Type Definition & Validation
apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go
New CopyOperatorAuthSecret *bool field added to RegistryConfig with kubebuilder validation and omitempty JSON tag. AuthSecret field comment expanded to document copying behavior and label requirements.
Deepcopy Generation
apis/controller/v1alpha1/zz_generated.deepcopy.go
BackupCronJobConfig.DeepCopyInto updated to deep-copy RegistryConfig via method call instead of shallow struct assignment. RegistryConfig.DeepCopyInto extended to deep-copy the new CopyOperatorAuthSecret field when non-nil.
Config Merge Logic
pkg/config/sync.go
mergeConfig now propagates CopyOperatorAuthSecret from source to destination registry config when present.
Secret Handling
pkg/secrets/backup.go
HandleRegistryAuthSecret derives copyOperatorAuthSecret (default false) and delegates to new EnsureRegistryAuthSecret function. New EnsureRegistryAuthSecret enforces "never overwrite" behavior: returns existing workspace secret if present; errors if absent and copying disabled; copies from operator secret if absent and copying enabled. CopySecret simplified to use direct Kubernetes Create and handle AlreadyExists by re-fetching.
CRD Schema Updates
deploy/bundle/manifests/...*, deploy/deployment/kubernetes/.../*, deploy/deployment/openshift/.../*, deploy/templates/crd/bases/...
OpenAPI schema extended with new copyOperatorAuthSecret boolean property and expanded authSecret descriptions documenting operator copying semantics, label requirements, non-overwrite guarantees, and default false behavior across all CRD manifests.
Test Fixtures & Coverage
controllers/backupcronjob/backupcronjob_controller_test.go, pkg/secrets/backup_test.go
BackupCronJobConfig.Registry fixtures updated to set CopyOperatorAuthSecret: pointer.Bool(true). New comprehensive Ginkgo test suite added for HandleRegistryAuthSecret covering nil/default, true, and false states with assertions for missing workspace secret errors, non-overwrite behavior, secret creation with labels/owner references, and existing secret return paths.

Sequence Diagram

sequenceDiagram
    participant BC as Backup Controller
    participant HSH as HandleRegistryAuthSecret
    participant ESH as EnsureRegistryAuthSecret
    participant K8s as Kubernetes API
    participant WS as Workspace Namespace

    BC->>HSH: HandleRegistryAuthSecret(config, workspace, operator-secret)
    HSH->>HSH: Determine copyOperatorAuthSecret flag<br/>(default: false)
    HSH->>ESH: EnsureRegistryAuthSecret(copyFlag, source-secret)
    
    ESH->>K8s: Get workspace secret<br/>by name
    alt Workspace secret exists
        K8s-->>ESH: Existing secret
        ESH-->>HSH: Return existing secret
    else Workspace secret missing
        alt copyOperatorAuthSecret = false
            ESH-->>HSH: Error: manual secret creation required
        else copyOperatorAuthSecret = true
            ESH->>K8s: Create secret in workspace<br/>from operator secret
            K8s->>WS: Write secret with labels<br/>& owner reference
            WS-->>K8s: Success (or AlreadyExists)
            K8s-->>ESH: Created/fetched secret
            ESH-->>HSH: Return workspace secret
        end
    end
    
    HSH-->>BC: Auth secret result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • ibuziuk

Poem

🐰 A clever rabbit hops with glee,
Adding flags for auth secrecy!
Copy or not—the choice is yours,
Secrets now pass through workspace doors. 🚪
No overwrites, just safety bright! ✨

🚥 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 describes the main feature: adding a conditional option to copy registry auth secrets from operator namespace to workspace namespace for backup operations.
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 unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented May 5, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: rohanKanojia
Once this PR has been reviewed and has the lgtm label, please assign dkwon17 for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@rohanKanojia rohanKanojia force-pushed the pr/secret-fallback-option branch from 56c6857 to f6f485c Compare May 5, 2026 11:13
@codecov
Copy link
Copy Markdown

codecov Bot commented May 5, 2026

Codecov Report

❌ Patch coverage is 64.51613% with 22 lines in your changes missing coverage. Please review.
✅ Project coverage is 36.94%. Comparing base (b3e2af5) to head (f6f485c).
⚠️ Report is 31 commits behind head on main.

Files with missing lines Patch % Lines
pkg/secrets/backup.go 69.81% 15 Missing and 1 partial ⚠️
apis/controller/v1alpha1/zz_generated.deepcopy.go 0.00% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1618      +/-   ##
==========================================
+ Coverage   35.48%   36.94%   +1.46%     
==========================================
  Files         168      168              
  Lines       14484    14726     +242     
==========================================
+ Hits         5139     5441     +302     
+ Misses       9006     8932      -74     
- Partials      339      353      +14     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rohanKanojia rohanKanojia force-pushed the pr/secret-fallback-option branch 3 times, most recently from 802c09c to 87f720c Compare May 5, 2026 15:16
@rohanKanojia rohanKanojia marked this pull request as ready for review May 5, 2026 15:50
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (4)
controllers/backupcronjob/backupcronjob_controller_test.go (1)

312-314: ⚡ Quick win

Add one unset/false-path test for CopyOperatorAuthSecret.

All touched executeBackupSync fixtures now force CopyOperatorAuthSecret=true, so this suite no longer validates the new default/disabled branch. Please add at least one case for unset/false (especially missing workspace secret) to guard the new behavior.

Also applies to: 345-346, 403-404, 440-441, 482-483, 514-516, 551-553, 588-589

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@controllers/backupcronjob/backupcronjob_controller_test.go` around lines 312
- 314, Tests in backupcronjob_controller_test.go currently set
CopyOperatorAuthSecret = pointer.Bool(true) in all executeBackupSync fixtures,
so the default/disabled branch (unset/false) and the missing workspace secret
path are not covered; add at least one test case for executeBackupSync where
CopyOperatorAuthSecret is either nil (unset) or pointer.Bool(false) and the
workspace secret is absent, then assert the controller follows the disabled
behavior (e.g., does not attempt to copy operator auth and returns the expected
status/condition). Update the relevant table/fixtures used by
TestExecuteBackupSync (or similarly named test harness) to include this
false/unset case and verify expected outcomes for missing workspace secret to
guard the new branch.
deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml (1)

222-240: ⚡ Quick win

Set an explicit CRD default for copyOperatorAuthSecret.

Line 239 says the field defaults to false, but the schema currently has no default: false. Adding it makes the API contract enforceable and avoids nil/merge ambiguity when the field is omitted.

Suggested schema tweak
                           copyOperatorAuthSecret:
+                            default: false
                             description: |-
                               CopyOperatorAuthSecret controls whether the operator should copy the authentication
                               secret from the operator namespace to the workspace namespace when it's not found
                               in the workspace namespace.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml`
around lines 222 - 240, The CRD schema for the boolean field
copyOperatorAuthSecret currently documents "Defaults to false" but lacks an
explicit default; update the OpenAPI schema for the field copyOperatorAuthSecret
(the property under the spec schema) to include default: false so the CRD
enforces the default value when the field is omitted, ensuring API
server/merging behavior matches the documented contract.
pkg/secrets/backup.go (2)

101-176: 💤 Low value

Logic and race handling look correct.

EnsureRegistryAuthSecret correctly: (1) never overwrites a user-provided secret, (2) returns a clear, actionable error when copyOperatorAuthSecret is false, (3) handles the Get→Create race via IsAlreadyExists re-fetch, and (4) sets the watch label and controller reference on the created secret.

Two minor observations (no action required, just flagging for awareness):

  • The relevant code snippet from pkg/provision/sync/sync.go shows SyncObjectWithCluster provides diff/update behavior. Bypassing it here is the right call since the explicit goal is to never overwrite — just make sure no future caller expects this function to reconcile drift in the workspace secret (e.g., if the operator's source secret rotates, the workspace copy will not be updated).
  • The error message at lines 125–134 references DevWorkspaceBackupAuthSecretName (the predefined name) for both the "not found" name and the "please create" name. That's consistent with the rest of the function but means the user-facing message will not mention the configured secretName from Registry.AuthSecret, which can be confusing if the admin configured a custom name. Consider including both names in the error.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/secrets/backup.go` around lines 101 - 176, Update the error returned when
copyOperatorAuthSecret is false in EnsureRegistryAuthSecret to include both the
workspace secret name (constants.DevWorkspaceBackupAuthSecretName) and the
configured/operator secret name (use sourceSecret.Name) so users see which
configured auth secret name the operator expects versus the name missing in the
workspace; change the fmt.Errorf in the !copyOperatorAuthSecret branch inside
EnsureRegistryAuthSecret to format and include sourceSecret.Name alongside
constants.DevWorkspaceBackupAuthSecretName.

30-30: ⚡ Quick win

Replace deprecated k8s.io/utils/pointer with k8s.io/utils/ptr.

golangci-lint (SA1019) reports that k8s.io/utils/pointer is deprecated. Use ptr.Deref instead of pointer.BoolDeref.

♻️ Proposed fix
-	"k8s.io/utils/pointer"
+	"k8s.io/utils/ptr"
-	copyOperatorAuthSecret := pointer.BoolDeref(
-		dwOperatorConfig.Workspace.BackupCronJob.Registry.CopyOperatorAuthSecret,
-		false,
-	)
+	copyOperatorAuthSecret := ptr.Deref(
+		dwOperatorConfig.Workspace.BackupCronJob.Registry.CopyOperatorAuthSecret,
+		false,
+	)
k8s.io/utils/ptr Deref function signature
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/secrets/backup.go` at line 30, Replace the deprecated import and calls to
pointer.*: change the import "k8s.io/utils/pointer" to "k8s.io/utils/ptr" and
update call sites using pointer.BoolDeref (and any other pointer.XDeref) to use
ptr.Deref instead, e.g. replace pointer.BoolDeref(someBoolPtr, false) with
ptr.Deref(someBoolPtr, false) in the functions/methods in pkg/secrets/backup.go.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml`:
- Around line 226-244: The CRD documents that copyOperatorAuthSecret defaults to
false but the schema lacks an enforced default; add a JSON schema default by
setting default: false for the copyOperatorAuthSecret property in the CRD (the
YAML under copyOperatorAuthSecret) so the API server will apply the default. If
the CRD is generated from Go types, add the kubebuilder default marker (e.g., //
+kubebuilder:default=false) to the Go field named CopyOperatorAuthSecret (or
copyOperatorAuthSecret) in the API type, regenerate the CRD manifests, and
verify the generated CRD includes default: false under the
copyOperatorAuthSecret schema.

In `@deploy/deployment/openshift/combined.yaml`:
- Around line 226-244: The CRD OpenAPI schema for the copyOperatorAuthSecret
boolean lacks an explicit default; add "default: false" to the property
definition in the combined.yaml CRD schema so the OpenAPIv3 spec matches the
documented and Go behavior (pointer.BoolDeref(..., false)); also update any CRD
description if needed to note this change is a breaking behavioral change for
users who relied on true.

In
`@deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml`:
- Around line 223-224: The sentence about the
"controller.devfile.io/watch-secret=true" label is ambiguous: update the text
that references authSecret to state this label requirement only applies to
operator-managed (copied) secrets when copyOperatorAuthSecret is enabled (i.e.,
the operator-created/copy-source path), and clarify that user-provided/manual
authSecret values do not need that label; reference the fields authSecret and
copyOperatorAuthSecret and explicitly mention "operator-managed/copy-source
secrets" and "user-provided/manual secrets" so readers know which secrets must
have controller.devfile.io/watch-secret=true.

In
`@deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml`:
- Around line 224-242: The CRD schema for the boolean property
copyOperatorAuthSecret declares "Defaults to false" in the description but lacks
an OpenAPI default; update the CRD schema for the copyOperatorAuthSecret
property by adding default: false alongside its type: boolean (i.e., under the
copyOperatorAuthSecret property in the CRD's schema) so the generated API docs
and schema reflect the described default behavior.

In `@pkg/secrets/backup.go`:
- Around line 57-61: The change introduced a breaking default (currently using
pointer.BoolDeref(..., false)) for
dwOperatorConfig.Workspace.BackupCronJob.Registry.CopyOperatorAuthSecret which
stops auto-copying the operator namespace secret; restore backward-compatible
behavior by changing the default to true in the copyOperatorAuthSecret
initialization (i.e., use pointer.BoolDeref(..., true)) so existing clusters
continue to auto-copy, and if you intentionally want false instead, update
docs/changelog and tests that rely on auto-copy (e.g.,
controllers/backupcronjob/backupcronjob_controller_test.go) to avoid upgrade
surprises.

---

Nitpick comments:
In `@controllers/backupcronjob/backupcronjob_controller_test.go`:
- Around line 312-314: Tests in backupcronjob_controller_test.go currently set
CopyOperatorAuthSecret = pointer.Bool(true) in all executeBackupSync fixtures,
so the default/disabled branch (unset/false) and the missing workspace secret
path are not covered; add at least one test case for executeBackupSync where
CopyOperatorAuthSecret is either nil (unset) or pointer.Bool(false) and the
workspace secret is absent, then assert the controller follows the disabled
behavior (e.g., does not attempt to copy operator auth and returns the expected
status/condition). Update the relevant table/fixtures used by
TestExecuteBackupSync (or similarly named test harness) to include this
false/unset case and verify expected outcomes for missing workspace secret to
guard the new branch.

In
`@deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml`:
- Around line 222-240: The CRD schema for the boolean field
copyOperatorAuthSecret currently documents "Defaults to false" but lacks an
explicit default; update the OpenAPI schema for the field copyOperatorAuthSecret
(the property under the spec schema) to include default: false so the CRD
enforces the default value when the field is omitted, ensuring API
server/merging behavior matches the documented contract.

In `@pkg/secrets/backup.go`:
- Around line 101-176: Update the error returned when copyOperatorAuthSecret is
false in EnsureRegistryAuthSecret to include both the workspace secret name
(constants.DevWorkspaceBackupAuthSecretName) and the configured/operator secret
name (use sourceSecret.Name) so users see which configured auth secret name the
operator expects versus the name missing in the workspace; change the fmt.Errorf
in the !copyOperatorAuthSecret branch inside EnsureRegistryAuthSecret to format
and include sourceSecret.Name alongside
constants.DevWorkspaceBackupAuthSecretName.
- Line 30: Replace the deprecated import and calls to pointer.*: change the
import "k8s.io/utils/pointer" to "k8s.io/utils/ptr" and update call sites using
pointer.BoolDeref (and any other pointer.XDeref) to use ptr.Deref instead, e.g.
replace pointer.BoolDeref(someBoolPtr, false) with ptr.Deref(someBoolPtr, false)
in the functions/methods in pkg/secrets/backup.go.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 58f3b582-37ca-446d-9ace-6bf37dec79a5

📥 Commits

Reviewing files that changed from the base of the PR and between fa371eb and 87f720c.

📒 Files selected for processing (12)
  • apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go
  • apis/controller/v1alpha1/zz_generated.deepcopy.go
  • controllers/backupcronjob/backupcronjob_controller_test.go
  • deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml
  • deploy/deployment/kubernetes/combined.yaml
  • deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml
  • deploy/deployment/openshift/combined.yaml
  • deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml
  • deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml
  • pkg/config/sync.go
  • pkg/secrets/backup.go
  • pkg/secrets/backup_test.go

Comment thread deploy/deployment/openshift/combined.yaml
Comment on lines 223 to 224
The secret must contain "controller.devfile.io/watch-secret=true" label so that it can be
recognized by the operator.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clarify label requirement scope for user-provided secrets

Line 223 currently reads as if all authSecret values must have controller.devfile.io/watch-secret=true. That conflicts with the manual-secret path when copyOperatorAuthSecret is disabled. Please scope this sentence to operator-managed/copy-source secrets only.

✏️ Proposed wording tweak
- The secret must contain "controller.devfile.io/watch-secret=true" label so that it can be
- recognized by the operator.
+ For operator-managed secret copying, the source secret in the operator namespace must contain
+ "controller.devfile.io/watch-secret=true" so the operator can recognize and manage it.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
The secret must contain "controller.devfile.io/watch-secret=true" label so that it can be
recognized by the operator.
For operator-managed secret copying, the source secret in the operator namespace must contain
"controller.devfile.io/watch-secret=true" so the operator can recognize and manage it.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml`
around lines 223 - 224, The sentence about the
"controller.devfile.io/watch-secret=true" label is ambiguous: update the text
that references authSecret to state this label requirement only applies to
operator-managed (copied) secrets when copyOperatorAuthSecret is enabled (i.e.,
the operator-created/copy-source path), and clarify that user-provided/manual
authSecret values do not need that label; reference the fields authSecret and
copyOperatorAuthSecret and explicitly mention "operator-managed/copy-source
secrets" and "user-provided/manual secrets" so readers know which secrets must
have controller.devfile.io/watch-secret=true.

Comment thread pkg/secrets/backup.go
rohanKanojia and others added 2 commits May 5, 2026 22:09
Add optional copyOperatorAuthSecret field to RegistryConfig to control
whether operator copies registry auth secrets to workspace namespaces.
Defaults to true for backward compatibility.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Rohan Kumar <rohaan@redhat.com>
Refactor CopySecret to respect copyOperatorAuthSecret flag and never
overwrite existing user secrets. When flag is false, return error
requiring users to provide their own credentials.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Rohan Kumar <rohaan@redhat.com>
@rohanKanojia rohanKanojia force-pushed the pr/secret-fallback-option branch from 87f720c to 0f8ddda Compare May 5, 2026 16:47
@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented May 5, 2026

@rohanKanojia: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/v14-che-happy-path 0f8ddda link true /test v14-che-happy-path

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Comment on lines +93 to +95
// If true: The operator will copy the secret from the operator namespace
// if it's not found in the workspace namespace. This provides automatic configuration
// but exposes operator-level credentials to workspace users.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think it's a bit verbose,

Can we update thee If true... section to:

If true, this enables the fallback mechanism where the operator will copy the secret from the operator namespace.

//
// If false (default): The operator will not copy the secret. Users must manually create a secret
// with the configured name in their workspace namespace. This is more secure as it allows
// users to provide scoped credentials with minimal privileges.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think it's a bit verbose,

Can we update the If false... section to:

If false, the operator will not copy the secret to workspace namespaces. 

// users to provide scoped credentials with minimal privileges.
//
// Note: Regardless of this setting, if a secret already exists in the workspace namespace,
// it will never be overwritten. User-provided secrets are always respected.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// it will never be overwritten. User-provided secrets are always respected.
// it will never be overwritten.

Comment thread pkg/secrets/backup.go
// Extract flag value (default: false, users must provide their own secret)
copyOperatorAuthSecret := pointer.BoolDeref(
dwOperatorConfig.Workspace.BackupCronJob.Registry.CopyOperatorAuthSecret,
false,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could we instead define the default field here?

pkg/config/defaults.go

Comment thread pkg/secrets/backup.go
return nil, fmt.Errorf(
"registry auth secret %q not found in workspace namespace %q. "+
"copyOperatorAuthSecret is set to false, so the operator will not copy the secret. "+
"Please manually create a secret of type kubernetes.io/dockerconfigjson with the name %q "+
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
"Please manually create a secret of type kubernetes.io/dockerconfigjson with the name %q "+
"Please create a secret of type kubernetes.io/dockerconfigjson with the name %q "+

Comment thread pkg/secrets/backup.go
if !copyOperatorAuthSecret {
return nil, fmt.Errorf(
"registry auth secret %q not found in workspace namespace %q. "+
"copyOperatorAuthSecret is set to false, so the operator will not copy the secret. "+
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
"copyOperatorAuthSecret is set to false, so the operator will not copy the secret. "+
"copyOperatorAuthSecret is set to false, the secret will not be copied. "+

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants