diff --git a/README.md b/README.md index f62ca972..56dca000 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,40 @@ To use NAC functionality: After NonAdminRestore completes, check if the application was successful restored by accessing its route and seeing its items in application UI. + - create NonAdminDownloadRequest to download logs and backup information + + For example, to download backup logs: + ```sh + oc apply -f - < + spec: + target: + kind: BackupLog + name: + EOF + ``` + + After the download request is processed, get the signed download URL and download the file: + ```sh + # Wait for processing + oc wait --for=condition=Processed nadr/backup-logs-download -n --timeout=300s + + # Get download URL and download file + DOWNLOAD_URL=$(oc get nadr backup-logs-download -n -o jsonpath='{.status.velero.status.downloadURL}') + wget "$DOWNLOAD_URL" -O backup-logs.tar.gz + ``` + + For detailed information about NonAdminDownloadRequest usage, supported download types, and troubleshooting, see the [NADR Usage Guide](docs/nadr_usage.md). + + Alternatively, use the automated download script: + ```sh + ./hack/nadr-download.sh -k BackupLog -n -ns + ``` + ## Notes on Non Admin Permissions and Enforcements ### Cluster Administrator Enforceable Spec Fields There are several types of cluster scoped objects that non-admin users should not have access to backup or restore. OADP self-service automatically excludes the following list of cluster scoped resources from being backed up or restored. diff --git a/config/samples/oadp_v1alpha1_nonadmindownloadrequest.yaml b/config/samples/oadp_v1alpha1_nonadmindownloadrequest.yaml index 9bd5a276..f9e75bc3 100644 --- a/config/samples/oadp_v1alpha1_nonadmindownloadrequest.yaml +++ b/config/samples/oadp_v1alpha1_nonadmindownloadrequest.yaml @@ -4,9 +4,18 @@ metadata: labels: app.kubernetes.io/name: oadp-nac app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/component: download-request name: nonadmindownloadrequest-sample spec: target: + # Download kind - options include: + # For backups: BackupLog, BackupContents, BackupVolumeSnapshots, + # BackupItemOperations, BackupResourceList, BackupResults, + # CSIBackupVolumeSnapshots, CSIBackupVolumeSnapshotContents, + # BackupVolumeInfos + # For restores: RestoreLog, RestoreResults, RestoreResourceList, + # RestoreItemOperations, RestoreVolumeInfo kind: BackupLog + # Name of the NonAdminBackup or NonAdminRestore to download from name: non-admin-backup-name diff --git a/docs/nadr_usage.md b/docs/nadr_usage.md new file mode 100644 index 00000000..2ba7d054 --- /dev/null +++ b/docs/nadr_usage.md @@ -0,0 +1,386 @@ +# NonAdminDownloadRequest (NADR) Usage Guide + +## Overview + +NonAdminDownloadRequest (NADR) allows non-admin users to download logs, backup contents, and other information related to their NonAdminBackup and NonAdminRestore operations. This feature provides secure, time-limited access to download resources through signed URLs. + +## Prerequisites + +- OADP operator version 1.5+ installed and configured +- NonAdminController (NAC) deployed and running +- Existing NonAdminBackup or NonAdminRestore resources +- NonAdminBackup must use NonAdminBackupStorageLocation (NABSL) + +## Supported Download Request Types + +### For NonAdminBackup Resources + +| **Download Kind** | **Description** | **Content** | +|-------------------|-----------------|-------------| +| `BackupLog` | Backup operation logs | Detailed logs from the backup process | +| `BackupContents` | Backup metadata and manifest | Complete backup metadata and resource manifests | +| `BackupVolumeSnapshots` | Volume snapshot information | Details about volume snapshots created | +| `BackupItemOperations` | Item operation details | Information about backup item operations | +| `BackupResourceList` | List of backed up resources | Complete list of resources included in backup | +| `BackupResults` | Backup execution results | Summary and results of backup operation | +| `CSIBackupVolumeSnapshots` | CSI volume snapshots | CSI-specific volume snapshot information | +| `CSIBackupVolumeSnapshotContents` | CSI snapshot contents | Detailed CSI volume snapshot contents | +| `BackupVolumeInfos` | Volume information | Information about volumes in the backup | + +### For NonAdminRestore Resources + +| **Download Kind** | **Description** | **Content** | +|-------------------|-----------------|-------------| +| `RestoreLog` | Restore operation logs | Detailed logs from the restore process | +| `RestoreResults` | Restore execution results | Summary and results of restore operation | +| `RestoreResourceList` | List of restored resources | Complete list of resources restored | +| `RestoreItemOperations` | Item operation details | Information about restore item operations | +| `RestoreVolumeInfo` | Volume information | Information about volumes in the restore | + +## Creating a NonAdminDownloadRequest + +### Basic NADR Structure + +```yaml +apiVersion: oadp.openshift.io/v1alpha1 +kind: NonAdminDownloadRequest +metadata: + name: + namespace: +spec: + target: + kind: # One of the supported kinds above + name: # Name of the NAB or NAR +``` + +### Example 1: Download Backup Logs + +```yaml +apiVersion: oadp.openshift.io/v1alpha1 +kind: NonAdminDownloadRequest +metadata: + name: backup-logs-download + namespace: my-app-namespace +spec: + target: + kind: BackupLog + name: my-backup +``` + +### Example 2: Download Backup Contents + +```yaml +apiVersion: oadp.openshift.io/v1alpha1 +kind: NonAdminDownloadRequest +metadata: + name: backup-contents-download + namespace: my-app-namespace +spec: + target: + kind: BackupContents + name: my-backup +``` + +### Example 3: Download Restore Logs + +```yaml +apiVersion: oadp.openshift.io/v1alpha1 +kind: NonAdminDownloadRequest +metadata: + name: restore-logs-download + namespace: my-app-namespace +spec: + target: + kind: RestoreLog + name: my-restore +``` + +## Creating and Managing Download Requests + +### Create a Download Request + +```bash +# Create from YAML file +oc apply -f nadr-example.yaml + +# Or create directly +oc create -f - < -n + +# Common causes: +# - Backup doesn't use NonAdminBackupStorageLocation +# - Referenced backup/restore doesn't exist +# - Permissions issues +``` + +#### 2. "NonAdminBackupStorageLocationNotUsed" Error + +**Problem**: Error condition shows backup doesn't use NABSL + +**Solution**: +- Ensure your NonAdminBackup uses a NonAdminBackupStorageLocation +- Recreate the NADR after fixing the backup configuration +- This is a terminal error - the NADR will not retry + +#### 3. "NonAdminBackupNotAvailable" or "NonAdminRestoreNotAvailable" + +**Problem**: Referenced backup or restore not found + +**Solution**: +```bash +# Verify the backup/restore exists +oc get nab -n +oc get nar -n + +# Check the name matches exactly in the NADR spec +``` + +#### 4. Download URL Not Available + +**Problem**: NADR status shows `Created` but no URL + +**Solution**: +```bash +# Check the underlying Velero DownloadRequest +oc get downloadrequest -n + +# Look for events +oc get events -n --field-selector involvedObject.name= +``` + +#### 5. URL Expired + +**Problem**: Download fails with 403 or similar error + +**Solution**: +```bash +# Check expiration time +oc get nadr -n -o jsonpath='{.status.velero.status.expiration}' + +# Create a new NADR if expired +``` + +### Debugging Commands + +```bash +# Check NADR status and conditions +oc get nadr -n +oc describe nadr -n + +# Check related Velero resources +oc get downloadrequest -n +oc get backup,restore -n + +# Check controller logs +oc logs -n deployment/oadp-non-admin-controller-manager + +# Check events +oc get events -n --sort-by='.lastTimestamp' +``` + +## Best Practices + +1. **Create NADR close to when you need to download** - URLs have expiration times +2. **Use descriptive names** - Makes it easier to track multiple download requests +3. **Clean up old NADRs** - Remove completed download requests to avoid clutter +4. **Check phases and conditions** - Always verify the NADR is processed before attempting download +5. **Handle URL expiration** - Implement retry logic in automation scripts +6. **Verify backup uses NABSL** - Ensure NonAdminBackups use NonAdminBackupStorageLocation + +## Helper Script + +For easier usage, a helper script is provided at `hack/nadr-download.sh` that automates the entire process: + +```bash +# Download backup logs +./hack/nadr-download.sh -k BackupLog -n my-backup -ns my-namespace + +# Download restore logs with verbose output +./hack/nadr-download.sh -k RestoreLog -n my-restore -ns my-namespace -v + +# Download backup contents to specific directory +./hack/nadr-download.sh -k BackupContents -n my-backup -ns my-namespace -o /tmp/downloads +``` + +The script handles: +- Creating the NonAdminDownloadRequest +- Waiting for processing completion +- Downloading the file using the signed URL +- Optional extraction of compressed files +- Cleanup of the download request + +## Integration with Automation + +### Example: Automated Backup and Download + +```bash +#!/bin/bash + +NAMESPACE="my-app" +BACKUP_NAME="automated-backup-$(date +%Y%m%d-%H%M%S)" + +# Create backup +oc apply -f - < -n -ns + +Download logs and other information from NonAdminBackup or NonAdminRestore resources. + +Required arguments: + -k, --kind Kind of download (e.g., BackupLog, RestoreLog, BackupContents, etc.) + -n, --name Name of the NonAdminBackup or NonAdminRestore + -ns, --namespace Namespace containing the backup/restore + +Optional arguments: + -r, --request-name Name for the download request (default: auto-generated) + -o, --output-dir Output directory for downloaded files (default: ./downloads) + -t, --timeout Timeout in seconds to wait for processing (default: 300) + -v, --verbose Enable verbose output + -h, --help Display this help message + +Supported download kinds: + Backup downloads: BackupLog, BackupContents, BackupVolumeSnapshots, + BackupItemOperations, BackupResourceList, BackupResults, + CSIBackupVolumeSnapshots, CSIBackupVolumeSnapshotContents, + BackupVolumeInfos + + Restore downloads: RestoreLog, RestoreResults, RestoreResourceList, + RestoreItemOperations, RestoreVolumeInfo + +Examples: + # Download backup logs + $0 -k BackupLog -n my-backup -ns my-app + + # Download restore logs with custom output directory + $0 -k RestoreLog -n my-restore -ns my-app -o /tmp/restore-logs + + # Download backup contents with verbose output + $0 -k BackupContents -n my-backup -ns my-app -v +EOF +} + +# Function for verbose output +log() { + if [ "$VERBOSE" = true ]; then + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" + fi +} + +# Function to generate output filename +generate_filename() { + local kind="$1" + local name="$2" + local timestamp=$(date +%Y%m%d-%H%M%S) + echo "${name}-${kind,,}-${timestamp}.tar.gz" +} + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + -k|--kind) + KIND="$2" + shift 2 + ;; + -n|--name) + NAME="$2" + shift 2 + ;; + -ns|--namespace) + NAMESPACE="$2" + shift 2 + ;; + -r|--request-name) + REQUEST_NAME="$2" + shift 2 + ;; + -o|--output-dir) + OUTPUT_DIR="$2" + shift 2 + ;; + -t|--timeout) + TIMEOUT="$2" + shift 2 + ;; + -v|--verbose) + VERBOSE=true + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown option: $1" + usage + exit 1 + ;; + esac +done + +# Validate required arguments +if [[ -z "$KIND" || -z "$NAME" || -z "$NAMESPACE" ]]; then + echo "Error: Missing required arguments" + usage + exit 1 +fi + +# Set default request name if not provided +if [[ -z "$REQUEST_NAME" ]]; then + REQUEST_NAME="${NAME,,}-${KIND,,}-download-$(date +%s)" +fi + +# Create output directory if it doesn't exist +mkdir -p "$OUTPUT_DIR" + +log "Starting download process for $KIND from $NAME in namespace $NAMESPACE" + +# Check if the referenced backup/restore exists +if [[ "$KIND" == *"Backup"* || "$KIND" == *"backup"* ]]; then + log "Checking if NonAdminBackup '$NAME' exists in namespace '$NAMESPACE'" + if ! oc get nab "$NAME" -n "$NAMESPACE" &> /dev/null; then + echo "Error: NonAdminBackup '$NAME' not found in namespace '$NAMESPACE'" + exit 1 + fi +else + log "Checking if NonAdminRestore '$NAME' exists in namespace '$NAMESPACE'" + if ! oc get nar "$NAME" -n "$NAMESPACE" &> /dev/null; then + echo "Error: NonAdminRestore '$NAME' not found in namespace '$NAMESPACE'" + exit 1 + fi +fi + +# Create the NonAdminDownloadRequest +log "Creating NonAdminDownloadRequest '$REQUEST_NAME'" +cat << EOF | oc apply -f - +apiVersion: oadp.openshift.io/v1alpha1 +kind: NonAdminDownloadRequest +metadata: + name: $REQUEST_NAME + namespace: $NAMESPACE + labels: + app.kubernetes.io/name: oadp-nac + app.kubernetes.io/component: download-request + created-by: nadr-download-script +spec: + target: + kind: $KIND + name: $NAME +EOF + +echo "NonAdminDownloadRequest '$REQUEST_NAME' created successfully" + +# Wait for the request to be processed +echo "Waiting for download request to be processed (timeout: ${TIMEOUT}s)..." +if ! oc wait --for=condition=Processed nadr/"$REQUEST_NAME" -n "$NAMESPACE" --timeout="${TIMEOUT}s"; then + echo "Error: Download request timed out or failed" + echo "Current status:" + oc describe nadr "$REQUEST_NAME" -n "$NAMESPACE" + exit 1 +fi + +# Check if the request was successful +PHASE=$(oc get nadr "$REQUEST_NAME" -n "$NAMESPACE" -o jsonpath='{.status.phase}') +if [ "$PHASE" != "Created" ]; then + echo "Error: Download request failed with phase: $PHASE" + echo "Details:" + oc describe nadr "$REQUEST_NAME" -n "$NAMESPACE" + exit 1 +fi + +# Get the download URL +log "Extracting download URL from NADR status" +DOWNLOAD_URL=$(oc get nadr "$REQUEST_NAME" -n "$NAMESPACE" -o jsonpath='{.status.velero.status.downloadURL}') + +if [ -z "$DOWNLOAD_URL" ]; then + echo "Error: No download URL available" + oc describe nadr "$REQUEST_NAME" -n "$NAMESPACE" + exit 1 +fi + +# Check URL expiration +EXPIRATION=$(oc get nadr "$REQUEST_NAME" -n "$NAMESPACE" -o jsonpath='{.status.velero.status.expiration}') +echo "Download URL expires at: $EXPIRATION" + +# Generate output filename +OUTPUT_FILE="$OUTPUT_DIR/$(generate_filename "$KIND" "$NAME")" + +# Download the file +echo "Downloading to: $OUTPUT_FILE" +log "Download URL: $DOWNLOAD_URL" + +if command -v wget &> /dev/null; then + wget "$DOWNLOAD_URL" -O "$OUTPUT_FILE" --progress=bar:force +elif command -v curl &> /dev/null; then + curl -L "$DOWNLOAD_URL" -o "$OUTPUT_FILE" --progress-bar +else + echo "Error: Neither wget nor curl is available" + exit 1 +fi + +if [ $? -eq 0 ]; then + echo "✓ Download completed successfully!" + echo " File: $OUTPUT_FILE" + echo " Size: $(du -h "$OUTPUT_FILE" | cut -f1)" + + # Offer to extract if it's a tar.gz file + if [[ "$OUTPUT_FILE" == *.tar.gz ]]; then + echo "" + read -p "Extract the downloaded file? (y/N): " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + EXTRACT_DIR="${OUTPUT_FILE%.tar.gz}" + mkdir -p "$EXTRACT_DIR" + tar -xzf "$OUTPUT_FILE" -C "$EXTRACT_DIR" + echo "✓ Extracted to: $EXTRACT_DIR" + fi + fi + + # Offer to cleanup the NADR + echo "" + read -p "Delete the download request '$REQUEST_NAME'? (y/N): " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + oc delete nadr "$REQUEST_NAME" -n "$NAMESPACE" + echo "✓ Download request deleted" + fi + +else + echo "✗ Download failed" + exit 1 +fi \ No newline at end of file diff --git a/hack/samples/downloads/README.md b/hack/samples/downloads/README.md new file mode 100644 index 00000000..1830a970 --- /dev/null +++ b/hack/samples/downloads/README.md @@ -0,0 +1,70 @@ +# NonAdminDownloadRequest (NADR) Samples + +This directory contains sample templates for creating NonAdminDownloadRequest resources to download various types of backup and restore information. + +## Available Samples + +### Backup-Related Downloads + +- **backup-logs.yaml** - Download backup operation logs +- **backup-contents.yaml** - Download backup metadata and manifest files +- **backup-resource-list.yaml** - Download list of resources included in backup + +### Restore-Related Downloads + +- **restore-logs.yaml** - Download restore operation logs + +## Usage + +All samples are OpenShift templates that can be processed with parameters: + +### Example: Download Backup Logs + +```bash +oc process -f backup-logs.yaml \ + -p NAMESPACE=my-app-namespace \ + -p BACKUP_NAME=my-backup \ + -p REQUEST_NAME=my-backup-logs \ + | oc apply -f - +``` + +### Example: Download Backup Contents + +```bash +oc process -f backup-contents.yaml \ + -p NAMESPACE=my-app-namespace \ + -p BACKUP_NAME=my-backup \ + | oc apply -f - +``` + +### Example: Download Restore Logs + +```bash +oc process -f restore-logs.yaml \ + -p NAMESPACE=my-app-namespace \ + -p RESTORE_NAME=my-restore \ + | oc apply -f - +``` + +## Parameters + +All templates support these parameters: + +- **NAMESPACE** (required) - Namespace containing the backup/restore +- **BACKUP_NAME/RESTORE_NAME** (required) - Name of the backup or restore +- **REQUEST_NAME** (optional) - Name for the download request (has defaults) + +## After Creation + +1. Wait for the download request to be processed: + ```bash + oc wait --for=condition=Processed nadr/ -n --timeout=300s + ``` + +2. Get the download URL and download the file: + ```bash + DOWNLOAD_URL=$(oc get nadr -n -o jsonpath='{.status.velero.status.downloadURL}') + wget "$DOWNLOAD_URL" -O downloaded-file.tar.gz + ``` + +For complete documentation, see [NADR Usage Guide](../../docs/nadr_usage.md). \ No newline at end of file diff --git a/hack/samples/downloads/backup-contents.yaml b/hack/samples/downloads/backup-contents.yaml new file mode 100644 index 00000000..259bb44b --- /dev/null +++ b/hack/samples/downloads/backup-contents.yaml @@ -0,0 +1,28 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: nadr-backup-contents +parameters: +- name: NAMESPACE + description: "Namespace where the NonAdminBackup was created" + required: true +- name: BACKUP_NAME + description: "Name of the NonAdminBackup to download contents from" + required: true +- name: REQUEST_NAME + description: "Name for the download request" + value: "backup-contents-download" +objects: +- apiVersion: oadp.openshift.io/v1alpha1 + kind: NonAdminDownloadRequest + metadata: + name: ${REQUEST_NAME} + namespace: ${NAMESPACE} + labels: + app.kubernetes.io/name: oadp-nac + app.kubernetes.io/component: download-request + download-type: backup-contents + spec: + target: + kind: BackupContents + name: ${BACKUP_NAME} \ No newline at end of file diff --git a/hack/samples/downloads/backup-logs.yaml b/hack/samples/downloads/backup-logs.yaml new file mode 100644 index 00000000..f1ea54b2 --- /dev/null +++ b/hack/samples/downloads/backup-logs.yaml @@ -0,0 +1,28 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: nadr-backup-logs +parameters: +- name: NAMESPACE + description: "Namespace where the NonAdminBackup was created" + required: true +- name: BACKUP_NAME + description: "Name of the NonAdminBackup to download logs from" + required: true +- name: REQUEST_NAME + description: "Name for the download request" + value: "backup-logs-download" +objects: +- apiVersion: oadp.openshift.io/v1alpha1 + kind: NonAdminDownloadRequest + metadata: + name: ${REQUEST_NAME} + namespace: ${NAMESPACE} + labels: + app.kubernetes.io/name: oadp-nac + app.kubernetes.io/component: download-request + download-type: backup-logs + spec: + target: + kind: BackupLog + name: ${BACKUP_NAME} \ No newline at end of file diff --git a/hack/samples/downloads/backup-resource-list.yaml b/hack/samples/downloads/backup-resource-list.yaml new file mode 100644 index 00000000..8ec76fea --- /dev/null +++ b/hack/samples/downloads/backup-resource-list.yaml @@ -0,0 +1,28 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: nadr-backup-resource-list +parameters: +- name: NAMESPACE + description: "Namespace where the NonAdminBackup was created" + required: true +- name: BACKUP_NAME + description: "Name of the NonAdminBackup to download resource list from" + required: true +- name: REQUEST_NAME + description: "Name for the download request" + value: "backup-resource-list-download" +objects: +- apiVersion: oadp.openshift.io/v1alpha1 + kind: NonAdminDownloadRequest + metadata: + name: ${REQUEST_NAME} + namespace: ${NAMESPACE} + labels: + app.kubernetes.io/name: oadp-nac + app.kubernetes.io/component: download-request + download-type: backup-resource-list + spec: + target: + kind: BackupResourceList + name: ${BACKUP_NAME} \ No newline at end of file diff --git a/hack/samples/downloads/restore-logs.yaml b/hack/samples/downloads/restore-logs.yaml new file mode 100644 index 00000000..cce3239b --- /dev/null +++ b/hack/samples/downloads/restore-logs.yaml @@ -0,0 +1,28 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: nadr-restore-logs +parameters: +- name: NAMESPACE + description: "Namespace where the NonAdminRestore was created" + required: true +- name: RESTORE_NAME + description: "Name of the NonAdminRestore to download logs from" + required: true +- name: REQUEST_NAME + description: "Name for the download request" + value: "restore-logs-download" +objects: +- apiVersion: oadp.openshift.io/v1alpha1 + kind: NonAdminDownloadRequest + metadata: + name: ${REQUEST_NAME} + namespace: ${NAMESPACE} + labels: + app.kubernetes.io/name: oadp-nac + app.kubernetes.io/component: download-request + download-type: restore-logs + spec: + target: + kind: RestoreLog + name: ${RESTORE_NAME} \ No newline at end of file