Skip to content

Commit

Permalink
adds envx, switch to backup-ns.sh/weekly label to w04 only (without Y…
Browse files Browse the repository at this point in the history
…YYY), snap labels, simulate test label generation
  • Loading branch information
majodev committed Jan 7, 2025
1 parent 1fb9f5c commit eb40485
Show file tree
Hide file tree
Showing 14 changed files with 255 additions and 28 deletions.
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# which kubectl version to install (should be in sync with the kubernetes version used by GKE)
# https://hub.docker.com/r/bitnami/kubectl/tags
FROM bitnami/kubectl:1.28 as kubectl
FROM bitnami/kubectl:1.29 as kubectl

### -----------------------
# --- Stage: development
Expand Down Expand Up @@ -235,8 +234,9 @@ ENV PATH $PATH:$KREW_ROOT/bin
# -> https://github.com/stern/stern
# -> https://github.com/ahmetb/kubectx
# -> https://github.com/patrickdappollonio/kubectl-slice
# -> https://github.com/majodev/kubectl-envx
# hack: we chown to $USERNAME to avoid permission issues when running the container
RUN kubectl krew install stern ctx ns slice \
RUN kubectl krew install stern ctx ns slice envx \
&& chown -R $USERNAME $KREW_ROOT

# typical k8s aliases/completions and other .bashrc modifications
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ kubectl label vs/<vs> "backup-ns.sh/monthly"-

# Add a specific label daily/weekly/monthly
kubectl label vs/<vs> "backup-ns.sh/daily"="YYYY-MM-DD"
kubectl label vs/<vs> "backup-ns.sh/weekly"="YYYY-w04"
kubectl label vs/<vs> "backup-ns.sh/weekly"="w04"
kubectl label vs/<vs> "backup-ns.sh/monthly"="YYYY-MM"

# Add a specific deleteAfter label (the pruner will delete the vs after the specified date)!
Expand Down
2 changes: 1 addition & 1 deletion cmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func runCreate(_ *cobra.Command, _ []string) {
runMySQLDump(config)
}

vsLabels := lib.GenerateVSLabels(config.Namespace, config.PVCName, config.LabelVS)
vsLabels := lib.GenerateVSLabels(config.Namespace, config.PVCName, config.LabelVS, time.Now())
vsAnnotations := lib.GenerateVSAnnotations(lib.GetBAKEnvVars())

vsObject := lib.GenerateVSObject(config.Namespace, config.VSClassName, config.PVCName, vsName, vsLabels, vsAnnotations)
Expand Down
2 changes: 1 addition & 1 deletion cmd/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func runDelete(_ *cobra.Command, args []string) {

log.Printf("Using namespace '%s'.\n", namespace)

if err := lib.PruneVolumeSnapshot(namespace, volumeSnapshotName); err != nil {
if err := lib.PruneVolumeSnapshot(namespace, volumeSnapshotName, true); err != nil {
log.Fatalf("Error deleting VolumeSnapshot: %v\n", err)
}

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ require (
github.com/google/uuid v1.6.0
github.com/pmezard/go-difflib v1.0.0
github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0
)

require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
72 changes: 72 additions & 0 deletions internal/lib/init_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package lib_test

import (
"fmt"
"log"
"os/exec"
"strings"
"sync"

"github.com/allaboutapps/backup-ns/internal/lib"
)

// Immediately error out if kubectl is currently not in the context of our kind test k8s cluster
Expand All @@ -15,4 +19,72 @@ func init() {
if context, _ := exec.Command("kubectl", "config", "current-context").Output(); !strings.Contains(string(context), "kind-backup-ns") {
log.Fatalf("kubectl is not currently running within the context of the kind test k8s cluster named 'kind-backup-ns', exit now!")
}

cleanupTestVolumeSnapshots()
}

func cleanupTestVolumeSnapshots() {
// Get all managed volume snapshots
vss, err := lib.GetManagedVolumeSnapshots()
if err != nil {
log.Fatalf("failed to get volume snapshots: %v", err)
}

// Filter snapshots in test namespaces
testNamespaces := map[string]bool{
"generic-test": true,
"postgres-test": true,
"mysql-test": true,
}

var filteredVss []lib.NamespacedK8sObject
for _, vs := range vss {
if testNamespaces[vs.Namespace] {
filteredVss = append(filteredVss, vs)
}
}

log.Printf("del %d test volume snapshots...\n", len(filteredVss))

const maxWorkers = 16
var wg sync.WaitGroup
jobs := make(chan lib.NamespacedK8sObject, len(filteredVss))
errors := make(chan error, len(filteredVss))

// Start workers
for w := 0; w < maxWorkers; w++ {
go func() {
for vs := range jobs {
if err := lib.PruneVolumeSnapshot(vs.Namespace, vs.Name, false); err != nil {
errors <- fmt.Errorf("failed to prune %s/%s: %w", vs.Namespace, vs.Name, err)
}
wg.Done()
}
}()
}

// Queue jobs
for _, vs := range filteredVss {
wg.Add(1)
jobs <- vs
}
close(jobs)

// Wait for completion and collect errors
go func() {
wg.Wait()
close(errors)
}()

// Check for errors
var errs []error
for err := range errors {
errs = append(errs, err)
}

if len(errs) > 0 {
log.Fatalf("errors during volume snapshot cleanup: %v", errs)
}

log.Printf("deleted %d test volume snapshots\n", len(filteredVss))
}
3 changes: 2 additions & 1 deletion internal/lib/mysql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os/exec"
"path/filepath"
"testing"
"time"

"github.com/allaboutapps/backup-ns/internal/lib"
)
Expand Down Expand Up @@ -53,7 +54,7 @@ func TestDumpAndRestoreMySQL(t *testing.T) {
t.Fatal("backup MySQL failed: ", err)
}

vsLabels := lib.GenerateVSLabels(namespace, "data", labelVSConfig)
vsLabels := lib.GenerateVSLabels(namespace, "data", labelVSConfig, time.Now())
vsAnnotations := lib.GenerateVSAnnotations(lib.GetBAKEnvVars())

vsObject := lib.GenerateVSObject(namespace, "csi-hostpath-snapclass", "data", vsName, vsLabels, vsAnnotations)
Expand Down
3 changes: 2 additions & 1 deletion internal/lib/postgres_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os/exec"
"path/filepath"
"testing"
"time"

"github.com/allaboutapps/backup-ns/internal/lib"
"github.com/allaboutapps/backup-ns/internal/test"
Expand Down Expand Up @@ -53,7 +54,7 @@ func TestDumpAndRestorePostgres(t *testing.T) {
t.Fatal("backup Postgres failed: ", err)
}

vsLabels := lib.GenerateVSLabels(namespace, "data", labelVSConfig)
vsLabels := lib.GenerateVSLabels(namespace, "data", labelVSConfig, time.Now())
vsAnnotations := lib.GenerateVSAnnotations(map[string]string{
"BAK_NAMESPACE": namespace,
"BAK_DB_POSTGRES": "true",
Expand Down
23 changes: 14 additions & 9 deletions internal/lib/vs.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func GenerateVSName(vsNameTemplate string, pvcName string, vsRand string) (strin
return buf.String(), nil
}

func GenerateVSLabels(namespace, pvcName string, config LabelVSConfig) map[string]string {
func GenerateVSLabels(namespace, pvcName string, config LabelVSConfig, now time.Time) map[string]string {
labels := map[string]string{
"backup-ns.sh/pvc": pvcName,
"backup-ns.sh/type": config.Type,
Expand All @@ -79,13 +79,13 @@ func GenerateVSLabels(namespace, pvcName string, config LabelVSConfig) map[strin
labels["backup-ns.sh/pod"] = config.Pod
}
if config.Retain == "daily_weekly_monthly" {
now := time.Now()
now := now
labels["backup-ns.sh/retain"] = "daily_weekly_monthly"

dailyLabel := now.Format("2006-01-02")

_, week := time.Now().ISOWeek()
weeklyLabel := now.Format("2006-") + fmt.Sprintf("w%02d", week)
_, week := now.ISOWeek()
weeklyLabel := fmt.Sprintf("w%02d", week)
monthlyLabel := now.Format("2006-01")

if !volumeSnapshotWithLabelValueExists(namespace, "backup-ns.sh/daily", dailyLabel) {
Expand All @@ -98,7 +98,7 @@ func GenerateVSLabels(namespace, pvcName string, config LabelVSConfig) map[strin
labels["backup-ns.sh/monthly"] = monthlyLabel
}
} else if config.Retain == "days" {
deleteAfter := time.Now().AddDate(0, 0, config.RetainDays).Format("2006-01-02")
deleteAfter := now.AddDate(0, 0, config.RetainDays).Format("2006-01-02")
labels["backup-ns.sh/retain"] = "days"
labels["backup-ns.sh/retain-days"] = strconv.Itoa(config.RetainDays)
labels["backup-ns.sh/delete-after"] = deleteAfter
Expand Down Expand Up @@ -296,8 +296,13 @@ func CreateVolumeSnapshot(namespace string, dryRun bool, vsName string, vsObject
return nil
}

func deleteVolumeSnapshot(namespace, volumeSnapshotName string) error {
deleteCmd := exec.Command("kubectl", "delete", "volumesnapshot", volumeSnapshotName, "-n", namespace)
func deleteVolumeSnapshot(namespace, volumeSnapshotName string, wait bool) error {
args := []string{"delete", "volumesnapshot", volumeSnapshotName, "-n", namespace}
if !wait {
args = append(args, "--wait=false")
}

deleteCmd := exec.Command("kubectl", args...)
output, err := deleteCmd.CombinedOutput()
if err != nil {
return fmt.Errorf("failed to delete VolumeSnapshot: %w, output: %s", err, output)
Expand All @@ -309,7 +314,7 @@ func deleteVolumeSnapshot(namespace, volumeSnapshotName string) error {
// Delete a VolumeSnapshot, its associated VolumeSnapshotContent and the underlying storage!
// This is a destructive operation and should be used with caution!
// This function will set the deletionPolicy of the VolumeSnapshotContent to "Delete" before deleting the VolumeSnapshot, thus ensuring the underlying storage is also deleted.
func PruneVolumeSnapshot(namespace, volumeSnapshotName string) error {
func PruneVolumeSnapshot(namespace, volumeSnapshotName string, wait bool) error {
// Get the VolumeSnapshotContent name
vscName, err := GetVolumeSnapshotContentName(namespace, volumeSnapshotName)
if err != nil {
Expand All @@ -322,7 +327,7 @@ func PruneVolumeSnapshot(namespace, volumeSnapshotName string) error {
}

// Delete the VolumeSnapshot
if err := deleteVolumeSnapshot(namespace, volumeSnapshotName); err != nil {
if err := deleteVolumeSnapshot(namespace, volumeSnapshotName, wait); err != nil {
return err
}

Expand Down
Loading

0 comments on commit eb40485

Please sign in to comment.