Skip to content
Draft
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
174 changes: 174 additions & 0 deletions labs/GSP1312/HASHNODE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# Protect your Google Cloud NetApp Volumes data with snapshots - GSP1312

## Overview

A NetApp Volumes snapshot preserves a point-in-time view of a filesystem. This lab uses a preprovisioned NFS volume and Linux VM to create a manual snapshot, restore an individual file, configure recurring snapshots, and revert the entire volume. Reversion is destructive to changes made after the snapshot; use **only the temporary lab resources**.

### What you'll do

- Mount the supplied NFS volume on vm1.
- Create sample files and a snapshot named snapshot1.
- Restore a deleted file from the snapshot directory.
- Configure hourly, daily, weekly, and monthly snapshot retention.
- Revert the lab volume to snapshot1 and verify recovery.

## Setup and requirements

### Before you click the Start Lab button

Open the [official NetApp snapshot lab](https://www.skills.google/focuses/124381?parent=catalog) in a private browser. Use **only the temporary credentials provided by the lab**; never paste a student password or personal project ID into public code. The lab provisions vm1 and volume1. Do not create duplicate resources or run the destructive revert on a production volume.

**Version note:** The public Google Skills page has inconsistent legacy checkpoint labels and sometimes displays a placeholder lab code. The five Task headings and procedures are consistent with the official indexed GSP1312 page, but checkpoint counts/labels must be confirmed in the active lab panel. No grader score is claimed.

### Activate Cloud Shell

Open Cloud Shell and verify the project, VM zone, and NetApp volume location:

```bash
gcloud config get-value project
gcloud compute instances list --filter='name=vm1' --format='table(name,zone,status)'
gcloud netapp volumes list --format='table(name,state,capacityGib)'
```

The volume's resource path includes its location. In the console, open NetApp Volumes → Volumes → volume1 → Mount instructions to obtain the unique NFS export IP and share path. Never invent an IP or region.

## Task 1. Mount the volume

Connect to vm1 using the Compute Engine SSH button or gcloud compute ssh with the detected zone. Install the NFS client utilities, create a mount point, and use the export path shown by your lab:

```bash
sudo apt-get update -qq
sudo apt-get install -y nfs-common
sudo mkdir -p /share1
sudo mount -t nfs -o rw,hard,rsize=65536,wsize=65536,vers=3,tcp '<NFS_IP>:/share1' /share1
df -h /share1
```

If the share is already mounted, do not mount it twice. The volume should initially show approximately 1 TiB.

### Test completed task

Click **Check my progress** for the mount objective when that checkpoint is available in your active lab.

## Task 2. Create a manual snapshot

On vm1, create two directories and two small files:

```bash
sudo mkdir -p /share1/folder1 /share1/folder2
printf 'important\n' | sudo tee /share1/file1.txt
printf 'top secret\n' | sudo tee /share1/file2.txt
sudo ls -la /share1
```

In the NetApp Volumes console, select volume1 → Create snapshot, name it snapshot1, and wait until it is ready. Alternatively, use the verified CLI commands with the volume location from the earlier discovery:

```bash
gcloud netapp volumes snapshots create snapshot1 --volume=volume1 --location='<VOLUME_LOCATION>'
gcloud netapp volumes snapshots describe snapshot1 --volume=volume1 --location='<VOLUME_LOCATION>' --format='value(state)'
```

Confirm the snapshot appears in the volume's Snapshots tab and reaches READY. Do not delete an existing snapshot of the same name merely to rerun the lab.

## Task 3. Restore a file from a snapshot

In volume1 → Edit → Snapshot configuration, ensure the snapshot directory is visible. The CLI equivalent is:

```bash
gcloud netapp volumes update volume1 --location='<VOLUME_LOCATION>' --snapshot-directory=true
```

On vm1, remove only the first sample file, inspect the snapshot directory, and restore it:

```bash
sudo rm -f /share1/file1.txt
sudo ls -la /share1
sudo ls -la /share1/.snapshot
sudo ls -la /share1/.snapshot/snapshot1
sudo cp /share1/.snapshot/snapshot1/file1.txt /share1/file1.txt
sudo ls -la /share1
```

The restored file should contain the original sample text. This is a file-level recovery and does not revert the whole volume.

## Task 4. Schedule the snapshots of a volume

Open volume1 → Edit → Snapshot configuration. Enable scheduled snapshots and enter the following settings:

| Schedule | Retain | Time |
|---|---:|---|
| Hourly | 24 | Minute 0 |
| Daily | 7 | 00:00 |
| Weekly | 8 | Sunday 00:00 |
| Monthly | 3 | Day 1 at 00:00 |

Save the volume. For CLI automation, the script uses the documented NetApp Volumes REST PATCH method with updateMask=snapshotPolicy, including enabled=true and all four schedules. It uses the current lab account's short-lived OAuth token and does not store credentials in the repository.

```bash
gcloud netapp volumes describe volume1 --location='<VOLUME_LOCATION>' --format='yaml(snapshotPolicy)'
```

Confirm the policy is enabled and the retention counts match the table.

### Test completed task

Click **Check my progress** for the scheduled snapshot/volume update objective when available. The public checkpoint panel has inconsistent labels; use the current lab UI as the source of truth.

## Task 5. Restore the entire volume from a snapshot

**Warning:** Reverting replaces the current volume contents with snapshot1's point-in-time state. This cannot be undone for changes made after that snapshot. Use only volume1 in the temporary lab.

On vm1, delete the four sample items and verify they are absent:

```bash
sudo rm -f /share1/file1.txt /share1/file2.txt
sudo rm -rf /share1/folder1 /share1/folder2
sudo ls -la /share1
```

In NetApp Volumes → volume1 → Snapshots, locate snapshot1 and select Revert. Review the warning and confirm by entering volume1. The CLI alternative is:

```bash
gcloud netapp volumes revert volume1 --location='<VOLUME_LOCATION>' --snapshot=snapshot1
```

After the operation completes, inspect the mounted share:

```bash
sudo ls -la /share1
sudo test -f /share1/file1.txt
sudo test -f /share1/file2.txt
```

The original files and directories should return. Verify the relevant completion objective in the active lab panel; no grader result has been verified.

---

## Solution of Lab

### Quick

After the Draft PR is merged into main, run exactly these two commands in an authorized lab Cloud Shell. The URL intentionally targets main and is unavailable until merge.

```bash
curl -LO https://raw.githubusercontent.com/ePlus-DEV/storage/refs/heads/main/labs/GSP1312/lab.sh
source lab.sh
```

The script discovers the project, VM zone, volume location, and NFS export; creates snapshot1, restores a file, configures the four schedules, and reverts the temporary volume. It performs bounded retries and enables FORCE_COLOR through tee while respecting NO_COLOR. The revert step is intentionally destructive to the lab volume's post-snapshot changes.

### Manual

Chưa tìm thấy video YouTube hướng dẫn thủ công đã xác minh cho lab này.

Follow all five manual Task sections above to complete the lab without a video.

## Sources and verification

- [Google Skills official NetApp snapshot lab](https://www.skills.google/focuses/124381?parent=catalog)
- [NetApp Volumes snapshot REST resource and states](https://docs.cloud.google.com/netapp/volumes/docs/reference/rest/v1/projects.locations.volumes.snapshots)
- [NetApp Volumes snapshot policy schema](https://docs.cloud.google.com/netapp/volumes/docs/reference/rest/v1/projects.locations.volumes)
- [NetApp Volumes PATCH API](https://docs.cloud.google.com/netapp/volumes/docs/reference/rest/v1/projects.locations.volumes/patch)
- [NetApp Volumes revert command](https://docs.cloud.google.com/sdk/gcloud/reference/netapp/volumes/revert)

**Status:** static validated only; runtime/grader unverified. The public page's legacy checkpoint labels need active-lab confirmation. No unverified screenshots or YouTube links are embedded. This is a Hashnode-ready draft, not a published article.
81 changes: 81 additions & 0 deletions labs/GSP1312/lab.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/usr/bin/env bash
# Copyright (c) 2026 ePlus.DEV | Unlicense
set -Eeuo pipefail
if [[ -v NO_COLOR ]]; then CYAN='' GREEN='' YELLOW='' RESET=''
elif [[ -t 1 || "${FORCE_COLOR:-0}" == 1 ]]; then
CYAN=$'\033[1;36m'; GREEN=$'\033[1;32m'; YELLOW=$'\033[1;33m'; RESET=$'\033[0m'
else CYAN='' GREEN='' YELLOW='' RESET=''; fi
log() { printf '%s%s%s\n' "$CYAN" "$*" "$RESET"; }
ok() { printf '%s%s%s\n' "$GREEN" "$*" "$RESET"; }
warn() { printf '%s%s%s\n' "$YELLOW" "$*" "$RESET"; }
trap 'warn "Failed at line $LINENO (exit $?)."' ERR
printf '%s\n' "${CYAN}ePlus.DEV – Starting Execution${RESET}" 'GSP1312 — NetApp snapshots (static validated only; runtime/grader unverified)'
for cmd in gcloud curl grep head; do command -v "$cmd" >/dev/null || { warn "Missing $cmd"; exit 1; }; done
PROJECT_ID="${PROJECT_ID:-$(gcloud config get-value project 2>/dev/null)}"
[[ -n "$PROJECT_ID" && "$PROJECT_ID" != '(unset)' ]] || { warn 'Choose an authorized lab project'; exit 1; }
VM=vm1
VOLUME=volume1
SNAPSHOT=snapshot1
ZONE="${ZONE:-$(gcloud compute instances list --project="$PROJECT_ID" --filter="name=$VM" --format='value(zone.basename())' | head -n 1)}"
[[ -n "$ZONE" ]] || { warn 'Preprovisioned vm1 not found'; exit 1; }
VOLUME_PATH="$(gcloud netapp volumes list --project="$PROJECT_ID" --format='value(name)' | grep '/volumes/volume1$' | head -n 1 || true)"
[[ "$VOLUME_PATH" =~ /locations/([^/]+)/volumes/volume1$ ]] || { warn 'Preprovisioned volume1 not found'; exit 1; }
LOCATION="${BASH_REMATCH[1]}"
NFS_EXPORT="${NFS_EXPORT:-$(gcloud netapp volumes describe "$VOLUME" --project="$PROJECT_ID" --location="$LOCATION" --format='value(mountOptions[0].exportFull)' 2>/dev/null || true)}"
if [[ -z "$NFS_EXPORT" && -t 0 ]]; then read -r -p 'NFS export from volume1 Mount instructions (IP:/share): ' NFS_EXPORT; fi
[[ "$NFS_EXPORT" =~ ^[0-9.]+:/[^[:space:]]+$ ]] || { warn 'Set NFS_EXPORT to the verified lab volume export'; exit 1; }
remote() {
local script="$1" attempt
for attempt in 1 2 3; do
if gcloud compute ssh "$VM" --project="$PROJECT_ID" --zone="$ZONE" --quiet --command="bash -lc $(printf '%q' "$script")"; then return 0; fi
warn "SSH retry $attempt/3"; sleep 8
done
return 1
}
wait_snapshot() {
local attempt state
for attempt in {1..24}; do
state="$(gcloud netapp volumes snapshots describe "$SNAPSHOT" --volume="$VOLUME" --location="$LOCATION" --project="$PROJECT_ID" --format='value(state)' 2>/dev/null || true)"
[[ "$state" == READY ]] && return 0
sleep 10
done
warn "Snapshot not READY (last: $state)"; return 1
}
log '== Task 1/5 — Mount volume1 on preprovisioned vm1 =='
remote "set -e; command -v mount.nfs >/dev/null || { sudo apt-get update -qq; sudo apt-get install -y -qq nfs-common; }; sudo mkdir -p /share1; if ! mountpoint -q /share1; then sudo mount -t nfs -o rw,hard,rsize=65536,wsize=65536,vers=3,tcp '$NFS_EXPORT' /share1; fi; df -h /share1"
ok 'NFS mount verified'
log '== Task 2/5 — Create files and manual snapshot1 =='
remote "set -e; sudo mkdir -p /share1/folder1 /share1/folder2; printf 'important\\n' | sudo tee /share1/file1.txt >/dev/null; printf 'top secret\\n' | sudo tee /share1/file2.txt >/dev/null; sudo ls -la /share1"
if ! gcloud netapp volumes snapshots describe "$SNAPSHOT" --volume="$VOLUME" --location="$LOCATION" --project="$PROJECT_ID" >/dev/null 2>&1; then
gcloud netapp volumes snapshots create "$SNAPSHOT" --volume="$VOLUME" --location="$LOCATION" --project="$PROJECT_ID" --quiet
fi
wait_snapshot
ok 'snapshot1 READY'
log '== Task 3/5 — Restore file1.txt from snapshot directory =='
gcloud netapp volumes update "$VOLUME" --project="$PROJECT_ID" --location="$LOCATION" --snapshot-directory=true --quiet
remote "set -e; sudo rm -f /share1/file1.txt; sudo test -f /share1/.snapshot/snapshot1/file1.txt; sudo cp /share1/.snapshot/snapshot1/file1.txt /share1/file1.txt; sudo test -f /share1/file1.txt; sudo ls -la /share1"
ok 'file1.txt restored'
log '== Task 4/5 — Configure hourly/daily/weekly/monthly snapshots =='
# The gcloud update command does not expose snapshotPolicy.enabled; patch only that policy.
POLICY_JSON='{"snapshotPolicy":{"enabled":true,"hourlySchedule":{"snapshotsToKeep":24,"minute":0},"dailySchedule":{"snapshotsToKeep":7,"hour":0,"minute":0},"weeklySchedule":{"snapshotsToKeep":8,"day":"Sunday","hour":0,"minute":0},"monthlySchedule":{"snapshotsToKeep":3,"daysOfMonth":"1","hour":0,"minute":0}}}'
TOKEN="$(gcloud auth print-access-token)"
[[ -n "$TOKEN" ]] || { warn 'No authorized lab access token'; exit 1; }
curl -fsS --retry 3 -X PATCH \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
"https://netapp.googleapis.com/v1/$VOLUME_PATH?updateMask=snapshotPolicy" \
-d "$POLICY_JSON" >/dev/null
unset TOKEN
for attempt in {1..24}; do
enabled="$(gcloud netapp volumes describe "$VOLUME" --project="$PROJECT_ID" --location="$LOCATION" --format='value(snapshotPolicy.enabled)' 2>/dev/null || true)"
[[ "$enabled" == 'True' || "$enabled" == 'true' ]] && break
sleep 10
done
[[ "$enabled" == 'True' || "$enabled" == 'true' ]] || { warn 'Snapshot policy not confirmed enabled'; exit 1; }
ok 'Scheduled snapshots enabled'
log '== Task 5/5 — Revert volume1 to snapshot1 =='
warn 'Revert is destructive to changes made after snapshot1, as required by this temporary lab.'
remote 'sudo rm -f /share1/file1.txt /share1/file2.txt; sudo rm -rf /share1/folder1 /share1/folder2; sudo ls -la /share1'
gcloud netapp volumes revert "$VOLUME" --location="$LOCATION" --project="$PROJECT_ID" --snapshot="$SNAPSHOT" --quiet
remote 'set -e; for i in $(seq 1 24); do if sudo test -f /share1/file1.txt && sudo test -f /share1/file2.txt; then sudo ls -la /share1; exit 0; fi; sleep 10; done; exit 1'
ok 'Reverted files confirmed. Use all four official Check my progress buttons.'
warn 'static validated only; runtime/grader unverified'
Loading