Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*.dylib

# Built binaries
oadp
kubectl-oadp
kubectl-oadp-linux-*
kubectl-oadp-darwin-*
Expand Down
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ help: ## Show this help message
@echo " make test-unit # Run unit tests only"
@echo " make test-integration # Run integration tests only"
@echo " make lint # Run golangci-lint checks"
@echo " make lint-fix # Run golangci-lint auto-fix and format code"
@echo ""
@echo "Release commands:"
@echo " make release-build # Build binaries for all platforms"
Expand Down Expand Up @@ -408,6 +409,14 @@ test-integration: ## Run integration tests only
lint: golangci-lint ## Run golangci-lint checks against all project's Go files
$(GOLANGCI_LINT) run ./...

.PHONY: lint-fix
lint-fix: golangci-lint ## Run golangci-lint auto-fix and format code
@echo "Running golangci-lint with auto-fix..."
$(GOLANGCI_LINT) run --fix ./...
@echo "Running go fmt..."
go fmt ./...
@echo "✅ Linting and formatting complete!"

# Cleanup targets
.PHONY: clean
clean: ## Remove built binaries and downloaded tools
Expand Down
76 changes: 76 additions & 0 deletions cmd/namespace_flag_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
Copyright 2025 The OADP CLI Contributors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmd

import (
"testing"

"github.com/migtools/oadp-cli/internal/testutil"
)

// TestNamespaceFlag tests that the -n/--namespace flag is available for admin commands
func TestNamespaceFlag(t *testing.T) {
binaryPath := testutil.BuildCLIBinary(t)

tests := []struct {
name string
args []string
expectContains []string
}{
{
name: "backup help shows namespace flag",
args: []string{"backup", "create", "--help"},
expectContains: []string{"-n, --namespace"},
},
{
name: "restore help shows namespace flag",
args: []string{"restore", "create", "--help"},
expectContains: []string{"-n, --namespace"},
},
{
name: "schedule help shows namespace flag",
args: []string{"schedule", "create", "--help"},
expectContains: []string{"-n, --namespace"},
},
{
name: "nabsl-request get help shows namespace flag",
args: []string{"nabsl-request", "get", "--help"},
expectContains: []string{"-n, --namespace"},
},
{
name: "nabsl-request approve help shows namespace flag",
args: []string{"nabsl-request", "approve", "--help"},
expectContains: []string{"-n, --namespace"},
},
{
name: "nabsl-request reject help shows namespace flag",
args: []string{"nabsl-request", "reject", "--help"},
expectContains: []string{"-n, --namespace"},
},
{
name: "nabsl-request describe help shows namespace flag",
args: []string{"nabsl-request", "describe", "--help"},
expectContains: []string{"-n, --namespace"},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
testutil.TestHelpCommand(t, binaryPath, tt.args, tt.expectContains)
})
}
}
147 changes: 147 additions & 0 deletions cmd/non-admin/backup/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# NonAdminBackup Create Command

## Overview

The `nonadmin backup create` command creates backup requests for non-admin users within their authorized namespaces.

## Minimal MVP Flags

The following flags represent the minimal viable product for backup creation:

### Resource Filtering

| Flag | Type | Default | Description | Status |
|------|------|---------|-------------|--------|
| `--include-resources` | StringArray | `["*"]` | Resources to include | ✅ MVP |
| `--exclude-resources` | StringArray | - | Resources to exclude | ✅ MVP |

### Label Selection

| Flag | Type | Default | Description | Status |
|------|------|---------|-------------|--------|
| `--selector`, `-l` | LabelSelector | - | Label selector filter | ✅ MVP |
| `--or-selector` | OrLabelSelector | - | OR label selectors | ✅ MVP |

### Cluster Resources

| Flag | Type | Default | Description | Status |
|------|------|---------|-------------|--------|
| `--include-cluster-resources` | OptionalBool | - | Include cluster resources (users can only set to false) | ✅ MVP |

### Timing & Storage

| Flag | Type | Default | Description | Status |
|------|------|---------|-------------|--------|
| `--ttl` | Duration | - | Backup retention time | ✅ MVP |
| `--storage-location` | String | - | NABSL reference | ✅ MVP |
| `--csi-snapshot-timeout` | Duration | - | CSI snapshot timeout | ✅ MVP |
| `--item-operation-timeout` | Duration | - | Async operation timeout | ✅ MVP |

### Snapshot Control

| Flag | Type | Default | Description | Status |
|------|------|---------|-------------|--------|
| `--snapshot-volumes` | OptionalBool | - | Enable volume snapshots | ✅ MVP |
| `--snapshot-move-data` | OptionalBool | - | Move snapshot data | ✅ MVP |
| `--default-volumes-to-fs-backup` | OptionalBool | - | Use filesystem backup | ✅ MVP |

### Control Flags

| Flag | Type | Default | Description | Status |
|------|------|---------|-------------|--------|
| `--force`, `-f` | Boolean | false | Skip storage-location requirement | ✅ MVP |

## Restricted Flags (Not Available)

The following flags are **restricted** for non-admin users per the NAB API restrictions:

| Flag | Reason | Doc Reference |
|------|--------|---------------|
| `--include-namespaces` | Restricted - automatically set to current namespace | NAB API docs |
| `--exclude-namespaces` | Restricted for non-admin users | NAB API docs |
| `--include-cluster-scoped-resources` | Restricted - only empty list acceptable | NAB API docs |
| `--volume-snapshot-locations` | Not supported - defaults used | NAB API docs |

## Flags Not in MVP (Future Enhancements)

The following flags are **allowed by the API** but not included in the minimal MVP:

### Metadata
| Flag | Admin Enforceable | Could Add Later |
|------|------------------|-----------------|
| `--labels` | ✅ Yes | Future |
| `--annotations` | ✅ Yes | Future |

### Advanced Features
| Flag | Admin Enforceable | Could Add Later |
|------|------------------|-----------------|
| `--from-schedule` | N/A | Future (requires schedule API) |
| `--ordered-resources` | ✅ Yes | Future |
| `--data-mover` | ✅ Yes | Future |
| `--resource-policies-configmap` | ✅ Yes | Future (admin-created only) |
| `--parallel-files-upload` | ✅ Yes | Future |

### Scoped Resources
| Flag | Admin Enforceable | Could Add Later |
|------|------------------|-----------------|
| `--exclude-cluster-scoped-resources` | ✅ Yes | Future |
| `--include-namespace-scoped-resources` | ✅ Yes | Future |
| `--exclude-namespace-scoped-resources` | ✅ Yes | Future |

## Examples

```bash
# Create a simple backup of all resources in the current namespace
oadp nonadmin backup create my-backup

# Create backup with specific resources
oadp nonadmin backup create my-backup \
--include-resources deployments,services

# Create backup with label selector
oadp nonadmin backup create my-backup \
--selector app=myapp

# Create backup with snapshots and TTL
oadp nonadmin backup create my-backup \
--snapshot-volumes \
--ttl 720h

# Create backup with specific storage location
oadp nonadmin backup create my-backup \
--storage-location my-nabsl

# Force creation with admin defaults (interactive confirmation)
oadp nonadmin backup create my-backup --force
```

## Architecture Notes

The backup create command uses **struct embedding** from Velero's backup CreateOptions, matching the pattern used in `nonadmin restore create`. This approach:
- Reduces code duplication
- Ensures compatibility with Velero updates
- Uses BindFlags() as the control gate to expose only MVP features to non-admin users
- Maintains forward compatibility for future enhancements

## Implementation Details

### Struct Embedding Pattern

```go
type CreateOptions struct {
*velerobackup.CreateOptions // Embed Velero's CreateOptions

// NAB-specific fields
Name string
Force bool
client kbclient.WithWatch
currentNamespace string
}
```

### MVP Flag Control

The `BindFlags()` method acts as a control gate, exposing only the MVP flags while the embedded struct contains all Velero options. This allows:
- Easy addition of new flags in the future (just bind them in BindFlags)
- Automatic compatibility with Velero struct updates
- Clear separation between what's exposed vs what's available
19 changes: 10 additions & 9 deletions cmd/non-admin/backup/backup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ func TestNonAdminBackupCommands(t *testing.T) {
"--include-resources",
"--exclude-resources",
"--force",
"--assume-yes",
},
},
{
Expand Down Expand Up @@ -243,19 +242,21 @@ func TestNonAdminBackupHelpFlags(t *testing.T) {
func TestNonAdminBackupCreateFlags(t *testing.T) {
binaryPath := testutil.BuildCLIBinary(t)

t.Run("create command has all expected flags", func(t *testing.T) {
t.Run("create command has all expected MVP flags", func(t *testing.T) {
expectedFlags := []string{
"--storage-location",
"--include-resources",
"--exclude-resources",
"--labels",
"--annotations",
"--force",
"--assume-yes",
"--snapshot-volumes",
"--ttl",
"--selector",
"--or-selector",
"--include-cluster-resources",
"--ttl",
"--storage-location",
"--csi-snapshot-timeout",
"--item-operation-timeout",
"--snapshot-volumes",
"--snapshot-move-data",
"--default-volumes-to-fs-backup",
"--force",
}

testutil.TestHelpCommand(t, binaryPath,
Expand Down
Loading
Loading