Skip to content

Add oadp setup command with RBAC detection and refactor builders#147

Merged
Joeavaikath merged 6 commits into
migtools:oadp-devfrom
Joeavaikath:issue-141-design-nonadmin-config
Mar 6, 2026
Merged

Add oadp setup command with RBAC detection and refactor builders#147
Joeavaikath merged 6 commits into
migtools:oadp-devfrom
Joeavaikath:issue-141-design-nonadmin-config

Conversation

@Joeavaikath
Copy link
Copy Markdown
Contributor

@Joeavaikath Joeavaikath commented Mar 5, 2026

Why the changes were made

Implements issue #141 by adding an oadp setup command that auto-detects whether users have cluster-wide admin permissions and configures the CLI accordingly. Previously, users had to manually run oc oadp client config set nonadmin=true, which was confusing and error-prone.

The detection uses RBAC permission checks (oc auth can-i create backups.velero.io --all-namespaces) to determine mode. Admin users can create Velero Backup resources cluster-wide, while non-admin users can only create NonAdminBackup resources in their namespace. This directly tests the actual permission difference rather than checking infrastructure visibility.

Also includes builder constructor refactoring to follow Go naming conventions (ForNonAdminBackupNewNonAdminBackupBuilder, ForNonAdminRestoreNewNonAdminRestoreBuilder) and adds default storage location configuration support for non-admin backups.

How to test the changes made

Build:

make build

Test setup command with admin permissions:

./kubectl-oadp setup
cat ~/.config/velero/config.json
# Verify: nonadmin=false

Test with non-admin permissions:

./kubectl-oadp setup --force
cat ~/.config/velero/config.json
# Verify: nonadmin=true

Test when not logged in:

oc logout
./kubectl-oadp setup --force
# Expected: Error with login instructions

Test config preservation:

oc oadp client config set default-nabsl=my-storage
./kubectl-oadp setup --force
cat ~/.config/velero/config.json
# Verify: default-nabsl still present

Verify builder refactoring:

make build
# Should compile without errors
# Existing backup/restore workflows should work unchanged

Summary by CodeRabbit

Release Notes

  • New Features
    • Added a new setup command that automatically detects admin vs non-admin user mode.
    • Users can now manage Velero client configuration with a --force flag option to override existing settings.
    • Configuration is persisted and tailored guidance is provided based on detected user permissions.

Joeavaikath and others added 4 commits March 5, 2026 15:09
Implements issue migtools#141 by adding an `oadp setup` command that automatically
detects whether the user has cluster-wide admin permissions and configures
the CLI accordingly.

Detection strategy:
- Attempts to list deployments across all namespaces
- If successful and finds openshift-adp-controller-manager → admin mode
- If permission denied/forbidden → non-admin mode
- If unauthorized (not logged in) → clear error with instructions

Key features:
- Auto-detects and configures nonadmin setting
- Preserves existing config fields (default-nabsl, etc)
- Available in both admin and non-admin modes
- --force flag to re-run detection
- Clear user feedback with next steps

Files added:
- cmd/setup/setup.go - Main setup command implementation
- cmd/setup/detector.go - Permission detection logic

Files modified:
- cmd/shared/factories.go - Added WriteVeleroClientConfig() and OADPNamespace field
- cmd/root.go - Registered setup command, added to allowed commands

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Joseph <jvaikath@redhat.com>
Renamed builder constructors from For* pattern to New*Builder pattern to
align with Go best practices where constructors should start with "New".

Changes:
- ForNonAdminBackup → NewNonAdminBackupBuilder
- ForNonAdminRestore → NewNonAdminRestoreBuilder
- Renamed parameter ns → namespace for clarity

Updated all usages in create.go files and example documentation.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Joseph <jvaikath@redhat.com>
Mention that the OADP controller deployment is not accessible for non-admin users.

Signed-off-by: Joseph <jvaikath@redhat.com>
Signed-off-by: Joseph <jvaikath@redhat.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 5, 2026

📝 Walkthrough

Walkthrough

Adds a new setup CLI command that detects admin vs non-admin mode via oc auth can-i and persists the result to the Velero client config. Also renames non-admin builder constructors to NewNonAdmin*Builder and updates call sites.

Changes

Cohort / File(s) Summary
Builder Constructor Renames
cmd/non-admin/backup/nonadminbackup_builder.go, cmd/non-admin/backup/create.go, cmd/non-admin/restore/nonadminrestore_builder.go, cmd/non-admin/restore/create.go
Renamed constructors from ForNonAdminBackup/ForNonAdminRestore to NewNonAdminBackupBuilder/NewNonAdminRestoreBuilder; parameter name standardized (nsnamespace) and call sites updated.
Setup Command & Detection
cmd/setup/setup.go, cmd/setup/detector.go
Added setup command, detection logic using oc auth can-i create backups.velero.io --all-namespaces, DetectionResult type, command lifecycle (flags, validate, run), and user-facing output.
Config Persistence & Root Integration
cmd/shared/factories.go, cmd/root.go
Added WriteVeleroClientConfig and helpers for reading/merging/writing client config; registered setup command in root and allowed it in non-admin access set.

Sequence Diagram

sequenceDiagram
    participant User
    participant CLI as "velero setup"
    participant OC as "oc auth can-i"
    participant Config as "client config file"

    User->>CLI: run `velero setup` (optional --force)
    CLI->>OC: execute `oc auth can-i create backups.velero.io --all-namespaces`
    alt OC returns "yes"
        OC-->>CLI: "yes"
        CLI->>CLI: DetectionResult{IsAdmin:true}
    else OC returns "no"
        OC-->>CLI: "no"
        CLI->>CLI: DetectionResult{IsAdmin:false}
    else OC errors (not logged in / forbidden)
        OC-->>CLI: error
        CLI->>CLI: DetectionResult{Error:...}
    end
    CLI->>Config: Read existing client config
    Config-->>CLI: existing config map
    CLI->>CLI: merge detection into config
    CLI->>Config: Write updated config (WriteVeleroClientConfig)
    Config-->>CLI: save confirmation
    CLI-->>User: print setup summary and guidance
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Nonadmin only #134 — touches non-admin CLI flow and client config/root command handling; likely related to setup/config changes.

Suggested labels

ai-gen-bugfix

Suggested reviewers

  • weshayutin
  • kaovilai

Poem

🐰 A rabbit peeks and gives a cheer,

I asked oc if the path was clear,
Builders renamed with tidy flair,
Config saved with thoughtful care,
Hops of code and carrots near!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately captures the main changes: adding a setup command with RBAC detection and refactoring builder constructors to follow Go naming conventions.
Description check ✅ Passed The description comprehensively covers both required sections: explains the rationale for the setup command and RBAC detection approach, and provides detailed testing instructions with multiple scenarios.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@Joeavaikath Joeavaikath linked an issue Mar 5, 2026 that may be closed by this pull request
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: 1

Caution

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

⚠️ Outside diff range comments (1)
cmd/non-admin/backup/nonadminbackup_builder.go (1)

29-29: ⚠️ Potential issue | 🟡 Minor

Example comment references old constructor name.

The example usage in the comment block still uses builder.ForNonAdminBackup but the constructor was renamed to NewNonAdminBackupBuilder.

📝 Proposed fix
-var nonAdminBackup = builder.ForNonAdminBackup("user-namespace", "backup-1").
+var nonAdminBackup = NewNonAdminBackupBuilder("user-namespace", "backup-1").
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/non-admin/backup/nonadminbackup_builder.go` at line 29, The example usage
still calls the old constructor name builder.ForNonAdminBackup; update it to the
new constructor NewNonAdminBackupBuilder (e.g. replace
builder.ForNonAdminBackup("user-namespace", "backup-1") with
builder.NewNonAdminBackupBuilder("user-namespace", "backup-1")) and search for
any other references to ForNonAdminBackup in nonadminbackup_builder.go to rename
them to NewNonAdminBackupBuilder accordingly so the example matches the current
API.
🧹 Nitpick comments (2)
cmd/shared/factories.go (1)

138-138: Consider using more restrictive file permissions.

The config file is written with 0644 permissions, making it world-readable. Since this is a user-specific configuration file that may contain cluster context information, consider using 0600 to restrict access to the owner only.

🔒 Proposed fix
-	if err := os.WriteFile(configPath, data, 0644); err != nil {
+	if err := os.WriteFile(configPath, data, 0600); err != nil {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/shared/factories.go` at line 138, The config file is created with
world-readable mode 0644; update the write in cmd/shared/factories.go (the
os.WriteFile call that writes to configPath) to use a more restrictive
permission (0600) so only the owner can read/write the file; ensure any tests or
callers that expect 0644 are adjusted and that this change is applied where
configPath is written (the os.WriteFile(..., data, 0644) invocation).
cmd/setup/detector.go (1)

61-61: Consider extracting the deployment name as a constant.

The hardcoded string "openshift-adp-controller-manager" is used to identify the OADP controller. Consider extracting this to a package-level constant for maintainability.

♻️ Proposed refactor
+const oadpControllerDeploymentName = "openshift-adp-controller-manager"
+
 // detectUserMode detects whether the user has admin permissions and finds the OADP namespace.
 ...
 	for _, deployment := range deployments.Items {
-		if deployment.Name == "openshift-adp-controller-manager" {
+		if deployment.Name == oadpControllerDeploymentName {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/setup/detector.go` at line 61, Extract the hardcoded deployment name
"openshift-adp-controller-manager" to a package-level constant (e.g., const
oadpControllerDeployment = "openshift-adp-controller-manager") and replace the
literal comparison in the code that checks deployment.Name (the if statement in
detector.go) with a comparison to that constant; add the constant near the top
of the file so any other uses in the package can reference it for
maintainability and update tests/usages accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/setup/setup.go`:
- Around line 163-164: Capture and handle the error returned by os.UserHomeDir()
instead of ignoring it: change the assignment to homeDir, err :=
os.UserHomeDir() and if err != nil set a safe fallback (e.g. homeDir = "~" or ""
), and optionally log or return the error; then build configPath using the
validated homeDir variable. This touches the homeDir and configPath usage in
cmd/setup/setup.go.

---

Outside diff comments:
In `@cmd/non-admin/backup/nonadminbackup_builder.go`:
- Line 29: The example usage still calls the old constructor name
builder.ForNonAdminBackup; update it to the new constructor
NewNonAdminBackupBuilder (e.g. replace
builder.ForNonAdminBackup("user-namespace", "backup-1") with
builder.NewNonAdminBackupBuilder("user-namespace", "backup-1")) and search for
any other references to ForNonAdminBackup in nonadminbackup_builder.go to rename
them to NewNonAdminBackupBuilder accordingly so the example matches the current
API.

---

Nitpick comments:
In `@cmd/setup/detector.go`:
- Line 61: Extract the hardcoded deployment name
"openshift-adp-controller-manager" to a package-level constant (e.g., const
oadpControllerDeployment = "openshift-adp-controller-manager") and replace the
literal comparison in the code that checks deployment.Name (the if statement in
detector.go) with a comparison to that constant; add the constant near the top
of the file so any other uses in the package can reference it for
maintainability and update tests/usages accordingly.

In `@cmd/shared/factories.go`:
- Line 138: The config file is created with world-readable mode 0644; update the
write in cmd/shared/factories.go (the os.WriteFile call that writes to
configPath) to use a more restrictive permission (0600) so only the owner can
read/write the file; ensure any tests or callers that expect 0644 are adjusted
and that this change is applied where configPath is written (the
os.WriteFile(..., data, 0644) invocation).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d73688be-c7df-4cfe-a7af-9da7422ada53

📥 Commits

Reviewing files that changed from the base of the PR and between 424c16a and 046fe65.

📒 Files selected for processing (8)
  • cmd/non-admin/backup/create.go
  • cmd/non-admin/backup/nonadminbackup_builder.go
  • cmd/non-admin/restore/create.go
  • cmd/non-admin/restore/nonadminrestore_builder.go
  • cmd/root.go
  • cmd/setup/detector.go
  • cmd/setup/setup.go
  • cmd/shared/factories.go

Comment thread cmd/setup/setup.go
@weshayutin
Copy link
Copy Markdown
Contributor

oc oadp setup
Detecting OADP configuration...

✓ Found OADP controller in namespace: openshift-adp
✓ Admin mode enabled

Configuration saved to: /home/whayutin/.config/velero/config.json

You can now use OADP admin commands:
  oc oadp backup create my-backup
  oc oadp restore create my-restore

non-admin check

nacuser1@fedora:~/git/oadp-cli$ git branch
* issue-141-design-nonadmin-config
  oadp-dev
nacuser1@fedora:~/git/oadp-cli$ make install
Building kubectl-oadp for current platform (linux/amd64)...
✅ Built kubectl-oadp successfully!
Installing kubectl-oadp to /home/nacuser1/.local/bin...
✅ Installed to /home/nacuser1/.local/bin

🔍 Checking PATH configuration...
   └─ ✅ /home/nacuser1/.local/bin is already in PATH


📋 Configuration:

   🔍 Detecting OADP deployment in cluster...
   ├─ ⚠️  Could not find openshift-adp-controller-manager deployment

   🔍 Looking for DataProtectionApplication (DPA) resources...
   ├─ ⚠️  Could not find DataProtectionApplication resources

   ⚠️  ⚠️  ⚠️
   ├─ ❌ OADP Operator is not detected in the cluster
   ├─ Fallback will check for Velero deployment as fallback
   ├─ Consider using the velero cli instead
   ⚠️  ⚠️  ⚠️

   🔍 Looking for Velero deployment as fallback...
   └─ ⚠️  Could not detect OADP or Velero deployment in cluster
   🤔 Which namespace should admin commands use for Velero resources?
   │  └─ (Common options: openshift-adp, velero, oadp)

   Enter namespace [default: openshift-adp]: 

   ├─ Setting Velero namespace to: openshift-adp
   └─ ✅ Client config initialized

🧪 Verifying installation...
   ├─ ✅ Installation verified: kubectl oadp plugin is accessible
   └─ Running version command...

      Error: unknown command "version" for "oadp"
      Run 'oadp --help' for usage.
      An error occurred: unknown command "version" for "oadp"

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🎉 Installation complete!

   Quick start:
   • oc oadp --help          # Show available commands
   • oc oadp backup get      # List backups
   • oc oadp version         # Show version info
nacuser1@fedora:~/git/oadp-cli$ oc oadp setup
Detecting OADP configuration...

OADP CLI is already configured.

Current mode: non-admin
Configuration file: /home/nacuser1/.config/velero/config.json

To reconfigure, run: oc oadp setup --force
oc oadp --help
OADP CLI commands

Usage:
  oc oadp [flags]
  oc oadp [command]

Available Commands:
  client      Velero client related commands
  completion  Generate the autocompletion script for the specified shell
  help        Help about any command
  nonadmin    Work with non-admin resources
  setup       Auto-detect and configure admin vs non-admin mode

oc oadp client config get
namespace: openshift-adp
nonadmin: true

weshayutin
weshayutin previously approved these changes Mar 5, 2026
Copy link
Copy Markdown
Contributor

@weshayutin weshayutin left a comment

Choose a reason for hiding this comment

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

/LGTM

Comment thread cmd/setup/detector.go Outdated
Detect if user has admin permissions by checking if they can create Velero Backup resources across all namespaces.

Signed-off-by: Joseph <jvaikath@redhat.com>
@Joeavaikath Joeavaikath changed the title Add oadp setup command and refactor builder constructors Add oadp setup command with RBAC detection and refactor builders Mar 6, 2026
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: 2

♻️ Duplicate comments (1)
cmd/setup/setup.go (1)

130-146: ⚠️ Potential issue | 🟡 Minor

Handle os.UserHomeDir() failures in both output helpers.

Both helpers ignore the error and can print an empty or misleading config path. Please use a safe fallback like ~/.config/velero/config.json or return the error from the caller.

📝 Suggested fix
+func configFileDisplayPath() string {
+	homeDir, err := os.UserHomeDir()
+	if err != nil {
+		return "~/.config/velero/config.json"
+	}
+	return filepath.Join(homeDir, ".config", "velero", "config.json")
+}
+
 // printCurrentConfig prints the current configuration
 func (o *SetupOptions) printCurrentConfig(config *shared.ClientConfig) {
-	homeDir, _ := os.UserHomeDir()
-	configPath := filepath.Join(homeDir, ".config", "velero", "config.json")
+	configPath := configFileDisplayPath()
 
 	if config.IsNonAdmin() {
 		fmt.Println("Current mode: non-admin")
@@
 // printSetupSuccess prints a success message after setup
 func (o *SetupOptions) printSetupSuccess() {
-	homeDir, _ := os.UserHomeDir()
-	configPath := filepath.Join(homeDir, ".config", "velero", "config.json")
+	configPath := configFileDisplayPath()
 
 	if o.detectionResult.IsAdmin {
 		fmt.Println("✓ Admin mode enabled")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/setup/setup.go` around lines 130 - 146, Both printCurrentConfig and
printSetupSuccess call os.UserHomeDir() and ignore its error, which can lead to
an empty/misleading config path; update these functions (printCurrentConfig and
printSetupSuccess) to check the error return from os.UserHomeDir() and on
failure use a safe fallback path ("~/.config/velero/config.json") or propagate
the error to the caller (choose consistent behavior across both helpers), then
build configPath using the resolved home (or the fallback) before printing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/setup/setup.go`:
- Around line 112-120: The code updates only config.NonAdmin but never persists
the detected namespace; copy the detected namespace from o.detectionResult (e.g.
o.detectionResult.Namespace) into the config appropriate field (e.g.
config.Namespace or config.OadpNamespace) before calling
shared.WriteVeleroClientConfig(config), and only assign it when the detected
namespace is non-empty so the CLI points to the controller namespace discovered
by detectUserMode().

In `@cmd/shared/factories.go`:
- Around line 116-141: WriteVeleroClientConfig currently overwrites
~/.config/velero/config.json and discards other Velero keys; change it to read
the existing config (os.ReadFile + json.Unmarshal into a
map[string]interface{}), merge only the keys your command owns (map keys
"namespace", "nonadmin", "default-nabsl" populated from the ClientConfig
instance) into that map, and then json.MarshalIndent and write the merged map
back to configPath (creating dirs if needed). Keep existing file
permissions/formatting behavior, handle the case where the file does not exist
by starting from an empty map, and preserve error handling for os.UserHomeDir,
os.MkdirAll, json marshal/unmarshal, and os.WriteFile; update function
references ClientConfig, WriteVeleroClientConfig, and configPath accordingly.

---

Duplicate comments:
In `@cmd/setup/setup.go`:
- Around line 130-146: Both printCurrentConfig and printSetupSuccess call
os.UserHomeDir() and ignore its error, which can lead to an empty/misleading
config path; update these functions (printCurrentConfig and printSetupSuccess)
to check the error return from os.UserHomeDir() and on failure use a safe
fallback path ("~/.config/velero/config.json") or propagate the error to the
caller (choose consistent behavior across both helpers), then build configPath
using the resolved home (or the fallback) before printing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5c2ab871-1a41-4531-b22f-fd81d488b017

📥 Commits

Reviewing files that changed from the base of the PR and between 046fe65 and 99cea29.

📒 Files selected for processing (3)
  • cmd/setup/detector.go
  • cmd/setup/setup.go
  • cmd/shared/factories.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • cmd/setup/detector.go

Comment thread cmd/setup/setup.go
Comment thread cmd/shared/factories.go Outdated
Comment thread cmd/setup/detector.go
Comment thread cmd/setup/setup.go
Signed-off-by: Joseph <jvaikath@redhat.com>
kaovilai
kaovilai previously approved these changes Mar 6, 2026
Copy link
Copy Markdown
Member

@kaovilai kaovilai left a comment

Choose a reason for hiding this comment

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

left some thoughts but works as is.

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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/shared/factories.go`:
- Around line 144-159: The mergeClientConfig function currently unconditionally
sets configMap["namespace"] = config.Namespace which can overwrite an existing
namespace with an empty value; update mergeClientConfig to handle "namespace"
consistently with "nonadmin" and "default-nabsl": check config.Namespace (via
ClientConfig.Namespace) and only set configMap["namespace"] when it is
non-empty, otherwise delete(configMap, "namespace") so the existing namespace is
preserved when the input is empty.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 303c9aab-7ec5-4905-a544-a096ba6123de

📥 Commits

Reviewing files that changed from the base of the PR and between 99cea29 and b518487.

📒 Files selected for processing (1)
  • cmd/shared/factories.go

Comment thread cmd/shared/factories.go
@Joeavaikath Joeavaikath merged commit 4f8d6b9 into migtools:oadp-dev Mar 6, 2026
16 checks passed
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.

design a way for users or tooling to set nonadmin:true

3 participants